Mercurial
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); |