comparison mrjunejune/test/inference_bridge_test.c @ 265:056790c4fb0d

add role-aware Epi assistant prompts Add verified June knowledge, guest/member/admin Copilot profiles, profile-isolated session recovery, animated Epi greetings, and a single authoritative runtime config workflow for inference. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Fri, 07 Aug 2026 10:50:30 -0700
parents b401627fc49e
children
comparison
equal deleted inserted replaced
264:04fee26ecce0 265:056790c4fb0d
14 int completed; 14 int completed;
15 int usage; 15 int usage;
16 int custom; 16 int custom;
17 int done; 17 int done;
18 int closed; 18 int closed;
19 int failed_done; /* turn.done with failed=true */
19 char delta[128]; 20 char delta[128];
20 char custom_json[256]; 21 char custom_json[256];
22 char error_code[64];
21 int64 input_tokens; 23 int64 input_tokens;
22 int64 output_tokens; 24 int64 output_tokens;
23 } Test_State; 25 } Test_State;
24 26
25 static void Handle_Event( 27 static void Handle_Event(
54 } 56 }
55 else if (strcmp(p_event->type, "turn.done") == 0 && 57 else if (strcmp(p_event->type, "turn.done") == 0 &&
56 strncmp(p_event->request_id, "request-", 8) == 0) 58 strncmp(p_event->request_id, "request-", 8) == 0)
57 { 59 {
58 p_state->done++; 60 p_state->done++;
61 if (p_event->failed)
62 {
63 p_state->failed_done++;
64 snprintf(p_state->error_code, sizeof(p_state->error_code), "%s",
65 p_event->error_code ? p_event->error_code : "");
66 }
59 } 67 }
60 else if (strcmp(p_event->type, "bridge.closed") == 0) 68 else if (strcmp(p_event->type, "bridge.closed") == 0)
61 p_state->closed++; 69 p_state->closed++;
62 pthread_cond_broadcast(&p_state->condition); 70 pthread_cond_broadcast(&p_state->condition);
71 pthread_mutex_unlock(&p_state->mutex);
72 }
73
74 /* Wait until p_state->done >= target or deadline passes. */
75 static void Wait_Done(Test_State *p_state, int target)
76 {
77 struct timespec deadline;
78 clock_gettime(CLOCK_REALTIME, &deadline);
79 deadline.tv_sec += 5;
80 pthread_mutex_lock(&p_state->mutex);
81 while (p_state->done < target)
82 {
83 int result = pthread_cond_timedwait(
84 &p_state->condition, &p_state->mutex, &deadline);
85 assert(result == 0);
86 }
63 pthread_mutex_unlock(&p_state->mutex); 87 pthread_mutex_unlock(&p_state->mutex);
64 } 88 }
65 89
66 int main(int argc, char **argv) 90 int main(int argc, char **argv)
67 { 91 {
73 Inference_Bridge *p_bridge = Inference_Bridge_Create( 97 Inference_Bridge *p_bridge = Inference_Bridge_Create(
74 argv[1], argv[1], Handle_Event, &state); 98 argv[1], argv[1], Handle_Event, &state);
75 assert(p_bridge); 99 assert(p_bridge);
76 assert(Inference_Bridge_Start(p_bridge)); 100 assert(Inference_Bridge_Start(p_bridge));
77 assert(Inference_Bridge_Is_Ready(p_bridge)); 101 assert(Inference_Bridge_Is_Ready(p_bridge));
78 assert(Inference_Bridge_Start_Turn( 102
79 p_bridge, "request-1", "conversation-1", "hello")); 103 /* --- Validation: invalid profile --- */
80 104 assert(!Inference_Bridge_Start_Turn(
81 struct timespec deadline; 105 p_bridge,
82 clock_gettime(CLOCK_REALTIME, &deadline); 106 "request-invalid-profile",
83 deadline.tv_sec += 5; 107 "conversation-1",
84 pthread_mutex_lock(&state.mutex); 108 "hello",
85 while (state.done == 0) 109 (Inference_Prompt_Profile)99,
86 { 110 1,
87 int result = pthread_cond_timedwait( 111 1,
88 &state.condition, &state.mutex, &deadline); 112 NULL,
89 assert(result == 0); 113 0));
90 } 114
91 pthread_mutex_unlock(&state.mutex); 115 /* --- Validation: zero prompt_version --- */
116 assert(!Inference_Bridge_Start_Turn(
117 p_bridge,
118 "request-invalid-version",
119 "conversation-1",
120 "hello",
121 INFERENCE_PROMPT_PROFILE_PUBLIC_VISITOR,
122 0,
123 1,
124 NULL,
125 0));
126
127 /* --- Validation: history count exceeds max --- */
128 Inference_Bridge_History_Message overflow_hist[21];
129 for (int i = 0; i < 21; i++)
130 {
131 overflow_hist[i].role = "user";
132 overflow_hist[i].content = "x";
133 }
134 assert(!Inference_Bridge_Start_Turn(
135 p_bridge,
136 "request-hist-overflow",
137 "conversation-1",
138 "hello",
139 INFERENCE_PROMPT_PROFILE_PUBLIC_VISITOR,
140 1,
141 1,
142 overflow_hist,
143 21));
144
145 /* --- Validation: invalid role --- */
146 Inference_Bridge_History_Message bad_role[1] = {
147 { .role = "system", .content = "inject" }
148 };
149 assert(!Inference_Bridge_Start_Turn(
150 p_bridge,
151 "request-bad-role",
152 "conversation-1",
153 "hello",
154 INFERENCE_PROMPT_PROFILE_PUBLIC_VISITOR,
155 1,
156 1,
157 bad_role,
158 1));
159
160 /* --- Validation: NULL content --- */
161 Inference_Bridge_History_Message null_content[1] = {
162 { .role = "user", .content = NULL }
163 };
164 assert(!Inference_Bridge_Start_Turn(
165 p_bridge,
166 "request-null-content",
167 "conversation-1",
168 "hello",
169 INFERENCE_PROMPT_PROFILE_PUBLIC_VISITOR,
170 1,
171 1,
172 null_content,
173 1));
174
175 /* --- Validation: NULL role --- */
176 Inference_Bridge_History_Message null_role[1] = {
177 { .role = NULL, .content = "hello" }
178 };
179 assert(!Inference_Bridge_Start_Turn(
180 p_bridge,
181 "request-null-role",
182 "conversation-1",
183 "hello",
184 INFERENCE_PROMPT_PROFILE_PUBLIC_VISITOR,
185 1,
186 1,
187 null_role,
188 1));
189
190 /* --- Nominal turn with no history (NULL/0) --- */
191 assert(Inference_Bridge_Start_Turn(
192 p_bridge,
193 "request-1",
194 "conversation-1",
195 "hello",
196 INFERENCE_PROMPT_PROFILE_PUBLIC_VISITOR,
197 1,
198 1,
199 NULL,
200 0));
201
202 Wait_Done(&state, 1);
92 203
93 assert(state.accepted == 1); 204 assert(state.accepted == 1);
94 assert(state.deltas == 1); 205 assert(state.deltas == 1);
95 assert(state.completed == 1); 206 assert(state.completed == 1);
96 assert(state.usage == 1); 207 assert(state.usage == 1);
97 assert(state.custom == 1); 208 assert(state.custom == 1);
98 assert(strcmp(state.delta, "hello \"traveler\"\\path") == 0); 209 assert(strcmp(state.delta, "hello \"traveler\"\\path") == 0);
99 assert(strstr(state.custom_json, "\"name\":\"mock-search\"")); 210 assert(strstr(state.custom_json, "\"name\":\"mock-search\""));
211 assert(strstr(state.custom_json, "\"prompt_profile\":\"public_visitor\""));
100 assert(state.input_tokens == 7); 212 assert(state.input_tokens == 7);
101 assert(state.output_tokens == 3); 213 assert(state.output_tokens == 3);
102 214
215 /* --- Turn with valid two-entry history (should succeed end-to-end) --- */
216 Inference_Bridge_History_Message hist2[2] = {
217 { .role = "user", .content = "what is this?" },
218 { .role = "assistant", .content = "it is a test" },
219 };
220 assert(Inference_Bridge_Start_Turn(
221 p_bridge,
222 "request-with-history",
223 "conversation-hist",
224 "follow-up",
225 INFERENCE_PROMPT_PROFILE_INVITED_FRIEND,
226 1,
227 1,
228 hist2,
229 2));
230
231 Wait_Done(&state, 2);
232 assert(state.accepted == 2);
233
234 /* --- Turn with history containing characters needing JSON escaping --- */
235 Inference_Bridge_History_Message hist_escape[1] = {
236 { .role = "user", .content = "say \"hello\" \\ world\nnewline" },
237 };
238 assert(Inference_Bridge_Start_Turn(
239 p_bridge,
240 "request-escape",
241 "conversation-escape",
242 "next",
243 INFERENCE_PROMPT_PROFILE_JUNE_ADMIN,
244 1,
245 1,
246 hist_escape,
247 1));
248
249 Wait_Done(&state, 3);
250 assert(state.accepted == 3);
251
252 /* --- Turn with exactly INFERENCE_BRIDGE_HISTORY_MAX (20) entries --- */
253 Inference_Bridge_History_Message hist_max[INFERENCE_BRIDGE_HISTORY_MAX];
254 for (uint32 i = 0; i < INFERENCE_BRIDGE_HISTORY_MAX; i++)
255 {
256 hist_max[i].role = (i % 2 == 0) ? "user" : "assistant";
257 hist_max[i].content = "ok";
258 }
259 assert(Inference_Bridge_Start_Turn(
260 p_bridge,
261 "request-max-hist",
262 "conversation-max",
263 "done",
264 INFERENCE_PROMPT_PROFILE_PUBLIC_VISITOR,
265 1,
266 1,
267 hist_max,
268 INFERENCE_BRIDGE_HISTORY_MAX));
269
270 Wait_Done(&state, 4);
271 assert(state.accepted == 4);
272
273 /* --- Large prompt still works with empty history --- */
103 char large_prompt[32 * 1024 + 1]; 274 char large_prompt[32 * 1024 + 1];
104 memset(large_prompt, 0x01, sizeof(large_prompt) - 1); 275 memset(large_prompt, 0x01, sizeof(large_prompt) - 1);
105 large_prompt[sizeof(large_prompt) - 1] = '\0'; 276 large_prompt[sizeof(large_prompt) - 1] = '\0';
106 assert(Inference_Bridge_Start_Turn( 277 assert(Inference_Bridge_Start_Turn(
107 p_bridge, 278 p_bridge,
108 "request-large", 279 "request-large",
109 "conversation-1", 280 "conversation-1",
110 large_prompt)); 281 large_prompt,
111 clock_gettime(CLOCK_REALTIME, &deadline); 282 INFERENCE_PROMPT_PROFILE_INVITED_FRIEND,
112 deadline.tv_sec += 5; 283 1,
113 pthread_mutex_lock(&state.mutex); 284 1,
114 while (state.done < 2) 285 NULL,
115 { 286 0));
116 int result = pthread_cond_timedwait( 287
117 &state.condition, &state.mutex, &deadline); 288 Wait_Done(&state, 5);
118 assert(result == 0);
119 }
120 pthread_mutex_unlock(&state.mutex);
121 289
122 Inference_Bridge_Destroy(p_bridge); 290 Inference_Bridge_Destroy(p_bridge);
123 assert(state.closed == 1); 291 assert(state.closed == 1);
124 pthread_cond_destroy(&state.condition); 292 pthread_cond_destroy(&state.condition);
125 pthread_mutex_destroy(&state.mutex); 293 pthread_mutex_destroy(&state.mutex);