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 }