Mercurial
comparison infinite_canvas/agent_response.c @ 281:c57149ad216e default tip
Copilot-Session: f68442b1-fa8f-46a0-9689-81710613bbd4
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 18 Aug 2026 22:18:15 -0700 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 280:49e9e591c9bb | 281:c57149ad216e |
|---|---|
| 1 #include "infinite_canvas/agent_response.h" | |
| 2 | |
| 3 #include <math.h> | |
| 4 #include <stdio.h> | |
| 5 #include <string.h> | |
| 6 | |
| 7 static boolean Canvas_Agent_Entity_Type( | |
| 8 const char *p_kind, | |
| 9 Canvas_Entity_Type *p_type) | |
| 10 { | |
| 11 struct { | |
| 12 const char *p_name; | |
| 13 Canvas_Entity_Type type; | |
| 14 } types[] = { | |
| 15 {"text", CANVAS_ENTITY_TEXT}, | |
| 16 {"button", CANVAS_ENTITY_BUTTON}, | |
| 17 {"text_area", CANVAS_ENTITY_TEXT_AREA}, | |
| 18 {"accordion", CANVAS_ENTITY_ACCORDION}, | |
| 19 {"card", CANVAS_ENTITY_CARD}, | |
| 20 {"calendar", CANVAS_ENTITY_CALENDAR}, | |
| 21 {"table", CANVAS_ENTITY_TABLE}, | |
| 22 {"notification", CANVAS_ENTITY_NOTIFICATION}, | |
| 23 {"scroll_area", CANVAS_ENTITY_SCROLL_AREA}, | |
| 24 {"image", CANVAS_ENTITY_IMAGE}, | |
| 25 {"web_content", CANVAS_ENTITY_WEB_CONTENT}, | |
| 26 }; | |
| 27 for (size_t index = 0; index < sizeof(types) / sizeof(types[0]); index++) { | |
| 28 if (strcmp(p_kind, types[index].p_name) == 0) { | |
| 29 *p_type = types[index].type; | |
| 30 return TRUE; | |
| 31 } | |
| 32 } | |
| 33 return FALSE; | |
| 34 } | |
| 35 | |
| 36 static boolean Canvas_Agent_JSON_Number( | |
| 37 Dowa_JSON_Entry *p_object, | |
| 38 const char *p_key, | |
| 39 float *p_value, | |
| 40 boolean *p_present) | |
| 41 { | |
| 42 Dowa_JSON_Value *p_json = Dowa_JSON_Get(p_object, p_key); | |
| 43 *p_present = p_json ? TRUE : FALSE; | |
| 44 if (!p_json) return TRUE; | |
| 45 if (p_json->type != DOWA_JSON_NUMBER || | |
| 46 !isfinite(p_json->num_val) || | |
| 47 fabs(p_json->num_val) > 10000000.0) { | |
| 48 return FALSE; | |
| 49 } | |
| 50 *p_value = (float)p_json->num_val; | |
| 51 return TRUE; | |
| 52 } | |
| 53 | |
| 54 static boolean Canvas_Agent_URL_Allowed(const char *p_value) | |
| 55 { | |
| 56 return !p_value[0] || | |
| 57 strncmp(p_value, "https://", 8) == 0 || | |
| 58 strncmp(p_value, "http://", 7) == 0; | |
| 59 } | |
| 60 | |
| 61 boolean Canvas_Agent_Response_Parse( | |
| 62 const char *p_json, | |
| 63 Canvas_Agent_Response *p_response) | |
| 64 { | |
| 65 if (!p_json || !p_response) return FALSE; | |
| 66 memset(p_response, 0, sizeof(*p_response)); | |
| 67 Dowa_Arena *p_arena = Dowa_Arena_Create(128 * 1024); | |
| 68 if (!p_arena) return FALSE; | |
| 69 Dowa_JSON_Value root = Dowa_JSON_Parse( | |
| 70 p_json, (int32)strlen(p_json), p_arena); | |
| 71 if (root.type != DOWA_JSON_OBJECT) { | |
| 72 Dowa_Arena_Free(p_arena); | |
| 73 return FALSE; | |
| 74 } | |
| 75 Dowa_JSON_Entry *p_object = root.object_val; | |
| 76 char *p_action = Dowa_JSON_Get_String(p_object, "action"); | |
| 77 char *p_presentation = | |
| 78 Dowa_JSON_Get_String(p_object, "presentation"); | |
| 79 char *p_title = Dowa_JSON_Get_String(p_object, "title"); | |
| 80 char *p_text = Dowa_JSON_Get_String(p_object, "response"); | |
| 81 Dowa_JSON_Value *p_conversation = | |
| 82 Dowa_JSON_Get(p_object, "conversation_id"); | |
| 83 if (!p_action || | |
| 84 (strcmp(p_action, "create") != 0 && | |
| 85 strcmp(p_action, "append") != 0) || | |
| 86 !p_presentation || | |
| 87 (strcmp(p_presentation, "conversation") != 0 && | |
| 88 strcmp(p_presentation, "action") != 0) || | |
| 89 (strcmp(p_action, "append") == 0 && | |
| 90 strcmp(p_presentation, "conversation") != 0) || | |
| 91 !p_text || | |
| 92 !p_conversation || | |
| 93 p_conversation->type != DOWA_JSON_NUMBER || | |
| 94 floor(p_conversation->num_val) != p_conversation->num_val || | |
| 95 p_conversation->num_val < 0 || | |
| 96 p_conversation->num_val > 4294967295.0) { | |
| 97 Dowa_Arena_Free(p_arena); | |
| 98 return FALSE; | |
| 99 } | |
| 100 snprintf( | |
| 101 p_response->action, | |
| 102 sizeof(p_response->action), | |
| 103 "%s", | |
| 104 p_action); | |
| 105 snprintf( | |
| 106 p_response->presentation, | |
| 107 sizeof(p_response->presentation), | |
| 108 "%s", | |
| 109 p_presentation); | |
| 110 snprintf( | |
| 111 p_response->title, | |
| 112 sizeof(p_response->title), | |
| 113 "%s", | |
| 114 p_title ? p_title : ""); | |
| 115 snprintf( | |
| 116 p_response->response, | |
| 117 sizeof(p_response->response), | |
| 118 "%s", | |
| 119 p_text); | |
| 120 p_response->conversation_id = (uint32)p_conversation->num_val; | |
| 121 | |
| 122 Dowa_JSON_Value *p_entities = Dowa_JSON_Get(p_object, "entities"); | |
| 123 if (p_entities) { | |
| 124 if (p_entities->type != DOWA_JSON_ARRAY || | |
| 125 Dowa_Array_Length(p_entities->array_val) > | |
| 126 CANVAS_AGENT_MAX_CREATED_ENTITIES) { | |
| 127 Dowa_Arena_Free(p_arena); | |
| 128 return FALSE; | |
| 129 } | |
| 130 for (size_t index = 0; | |
| 131 index < Dowa_Array_Length(p_entities->array_val); | |
| 132 index++) { | |
| 133 Dowa_JSON_Value *p_value = &p_entities->array_val[index]; | |
| 134 if (p_value->type != DOWA_JSON_OBJECT) { | |
| 135 Dowa_Arena_Free(p_arena); | |
| 136 return FALSE; | |
| 137 } | |
| 138 Dowa_JSON_Entry *p_entity_object = p_value->object_val; | |
| 139 char *p_kind = | |
| 140 Dowa_JSON_Get_String(p_entity_object, "kind"); | |
| 141 Canvas_Agent_Entity_Spec *p_spec = | |
| 142 &p_response->entities[p_response->entity_count]; | |
| 143 if (!p_kind || | |
| 144 !Canvas_Agent_Entity_Type(p_kind, &p_spec->type)) { | |
| 145 Dowa_Arena_Free(p_arena); | |
| 146 return FALSE; | |
| 147 } | |
| 148 char *p_label = | |
| 149 Dowa_JSON_Get_String(p_entity_object, "label"); | |
| 150 char *p_entity_text = | |
| 151 Dowa_JSON_Get_String(p_entity_object, "text"); | |
| 152 snprintf( | |
| 153 p_spec->label, | |
| 154 sizeof(p_spec->label), | |
| 155 "%s", | |
| 156 p_label ? p_label : ""); | |
| 157 snprintf( | |
| 158 p_spec->text, | |
| 159 sizeof(p_spec->text), | |
| 160 "%s", | |
| 161 p_entity_text ? p_entity_text : ""); | |
| 162 if ((p_spec->type == CANVAS_ENTITY_IMAGE || | |
| 163 p_spec->type == CANVAS_ENTITY_WEB_CONTENT) && | |
| 164 !Canvas_Agent_URL_Allowed(p_spec->text)) { | |
| 165 Dowa_Arena_Free(p_arena); | |
| 166 return FALSE; | |
| 167 } | |
| 168 float x = 0.0f; | |
| 169 float y = 0.0f; | |
| 170 boolean has_x = FALSE; | |
| 171 boolean has_y = FALSE; | |
| 172 if (!Canvas_Agent_JSON_Number( | |
| 173 p_entity_object, "x", &x, &has_x) || | |
| 174 !Canvas_Agent_JSON_Number( | |
| 175 p_entity_object, "y", &y, &has_y)) { | |
| 176 Dowa_Arena_Free(p_arena); | |
| 177 return FALSE; | |
| 178 } | |
| 179 if (has_x != has_y) { | |
| 180 Dowa_Arena_Free(p_arena); | |
| 181 return FALSE; | |
| 182 } | |
| 183 if ((has_x && (fabsf(x) > 1000000.0f || | |
| 184 fabsf(y) > 1000000.0f))) { | |
| 185 Dowa_Arena_Free(p_arena); | |
| 186 return FALSE; | |
| 187 } | |
| 188 p_spec->has_position = has_x && has_y; | |
| 189 p_spec->position = (Vector2){x, y}; | |
| 190 float width = 0.0f; | |
| 191 float height = 0.0f; | |
| 192 boolean has_width = FALSE; | |
| 193 boolean has_height = FALSE; | |
| 194 if (!Canvas_Agent_JSON_Number( | |
| 195 p_entity_object, | |
| 196 "width", | |
| 197 &width, | |
| 198 &has_width) || | |
| 199 !Canvas_Agent_JSON_Number( | |
| 200 p_entity_object, | |
| 201 "height", | |
| 202 &height, | |
| 203 &has_height)) { | |
| 204 Dowa_Arena_Free(p_arena); | |
| 205 return FALSE; | |
| 206 } | |
| 207 if (has_width != has_height || | |
| 208 (has_width && | |
| 209 (width < 32.0f || | |
| 210 height < 24.0f || | |
| 211 width > 5000.0f || | |
| 212 height > 5000.0f))) { | |
| 213 Dowa_Arena_Free(p_arena); | |
| 214 return FALSE; | |
| 215 } | |
| 216 p_spec->has_size = has_width && has_height; | |
| 217 p_spec->size = (Vector2){width, height}; | |
| 218 Dowa_JSON_Value *p_entity_value = | |
| 219 Dowa_JSON_Get(p_entity_object, "value"); | |
| 220 if (p_entity_value) { | |
| 221 if (p_entity_value->type != DOWA_JSON_NUMBER || | |
| 222 floor(p_entity_value->num_val) != | |
| 223 p_entity_value->num_val || | |
| 224 p_entity_value->num_val < -2147483648.0 || | |
| 225 p_entity_value->num_val > 2147483647.0) { | |
| 226 Dowa_Arena_Free(p_arena); | |
| 227 return FALSE; | |
| 228 } | |
| 229 p_spec->value = (int32)p_entity_value->num_val; | |
| 230 p_spec->has_value = TRUE; | |
| 231 } | |
| 232 p_response->entity_count++; | |
| 233 } | |
| 234 } | |
| 235 Dowa_Arena_Free(p_arena); | |
| 236 return TRUE; | |
| 237 } |