@garrix82/reactgenie-dsl 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/.env +10 -0
  2. package/.env.example +17 -0
  3. package/.github/workflows/publish.yml +20 -0
  4. package/package.json +1 -5
  5. package/dist/__test__/dsl-descriptor.test.d.ts +0 -1
  6. package/dist/__test__/dsl-descriptor.test.js +0 -27
  7. package/dist/__test__/dsl-descriptor.test.js.map +0 -1
  8. package/dist/__test__/example_descriptor.d.ts +0 -125
  9. package/dist/__test__/example_descriptor.js +0 -607
  10. package/dist/__test__/example_descriptor.js.map +0 -1
  11. package/dist/__test__/food_descriptor.state.json +0 -1
  12. package/dist/__test__/food_descriptor.test.d.ts +0 -74
  13. package/dist/__test__/food_descriptor.test.js +0 -205
  14. package/dist/__test__/food_descriptor.test.js.map +0 -1
  15. package/dist/__test__/nl-interpreter-provider-selection.test.d.ts +0 -1
  16. package/dist/__test__/nl-interpreter-provider-selection.test.js +0 -73
  17. package/dist/__test__/nl-interpreter-provider-selection.test.js.map +0 -1
  18. package/dist/__test__/nl-interpreter.test.d.ts +0 -1
  19. package/dist/__test__/nl-interpreter.test.js +0 -86
  20. package/dist/__test__/nl-interpreter.test.js.map +0 -1
  21. package/dist/decorators/__test__/decorators.test.d.ts +0 -1
  22. package/dist/decorators/__test__/decorators.test.js +0 -182
  23. package/dist/decorators/__test__/decorators.test.js.map +0 -1
  24. package/dist/decorators/__test__/inheritance-descriptor.test.d.ts +0 -1
  25. package/dist/decorators/__test__/inheritance-descriptor.test.js +0 -107
  26. package/dist/decorators/__test__/inheritance-descriptor.test.js.map +0 -1
  27. package/dist/dsl/__test__/dsl-interpreter.test.d.ts +0 -1
  28. package/dist/dsl/__test__/dsl-interpreter.test.js +0 -334
  29. package/dist/dsl/__test__/dsl-interpreter.test.js.map +0 -1
  30. package/dist/dsl/__test__/parser.gen.test.d.ts +0 -1
  31. package/dist/dsl/__test__/parser.gen.test.js +0 -283
  32. package/dist/dsl/__test__/parser.gen.test.js.map +0 -1
  33. package/dist/nl/__test__/context-aware-prompt.test.d.ts +0 -1
  34. package/dist/nl/__test__/context-aware-prompt.test.js +0 -247
  35. package/dist/nl/__test__/context-aware-prompt.test.js.map +0 -1
  36. package/dist/nl/__test__/context-selector.test.d.ts +0 -1
  37. package/dist/nl/__test__/context-selector.test.js +0 -20
  38. package/dist/nl/__test__/context-selector.test.js.map +0 -1
  39. package/dist/nl/__test__/nl-parser-groq-transport.test.d.ts +0 -1
  40. package/dist/nl/__test__/nl-parser-groq-transport.test.js +0 -87
  41. package/dist/nl/__test__/nl-parser-groq-transport.test.js.map +0 -1
  42. package/dist/nl/__test__/nl-parser-openai-parity.test.d.ts +0 -1
  43. package/dist/nl/__test__/nl-parser-openai-parity.test.js +0 -206
  44. package/dist/nl/__test__/nl-parser-openai-parity.test.js.map +0 -1
  45. package/dist/nl/__test__/nl-parser-openai-sampling.test.d.ts +0 -1
  46. package/dist/nl/__test__/nl-parser-openai-sampling.test.js +0 -44
  47. package/dist/nl/__test__/nl-parser-openai-sampling.test.js.map +0 -1
  48. package/dist/nl/__test__/nl-parser-openai-transport.test.d.ts +0 -1
  49. package/dist/nl/__test__/nl-parser-openai-transport.test.js +0 -55
  50. package/dist/nl/__test__/nl-parser-openai-transport.test.js.map +0 -1
  51. package/dist/nl/__test__/nl-parser-utils.test.d.ts +0 -1
  52. package/dist/nl/__test__/nl-parser-utils.test.js +0 -70
  53. package/dist/nl/__test__/nl-parser-utils.test.js.map +0 -1
  54. package/dist/nl/__test__/nl-parser.test.d.ts +0 -1
  55. package/dist/nl/__test__/nl-parser.test.js +0 -64
  56. package/dist/nl/__test__/nl-parser.test.js.map +0 -1
  57. package/dist/nl/__test__/parameter-tuning.test.d.ts +0 -1
  58. package/dist/nl/__test__/parameter-tuning.test.js +0 -95
  59. package/dist/nl/__test__/parameter-tuning.test.js.map +0 -1
  60. package/dist/nl/__test__/semantic-parsing-experiment.test.d.ts +0 -1
  61. package/dist/nl/__test__/semantic-parsing-experiment.test.js +0 -178
  62. package/dist/nl/__test__/semantic-parsing-experiment.test.js.map +0 -1
  63. package/dist/nl/llm-monitoring.test.d.ts +0 -5
  64. package/dist/nl/llm-monitoring.test.js +0 -101
  65. package/dist/nl/llm-monitoring.test.js.map +0 -1
  66. package/lib/__test__/dsl-descriptor.test.ts +0 -27
  67. package/lib/__test__/example_descriptor.ts +0 -762
  68. package/lib/__test__/food_descriptor.state.json +0 -1
  69. package/lib/__test__/food_descriptor.test.ts +0 -331
  70. package/lib/__test__/nl-interpreter-provider-selection.test.ts +0 -126
  71. package/lib/__test__/nl-interpreter.test.ts +0 -129
  72. package/lib/decorators/__test__/decorators.test.ts +0 -177
  73. package/lib/decorators/__test__/inheritance-descriptor.test.ts +0 -92
  74. package/lib/decorators/decorators.ts +0 -754
  75. package/lib/decorators/index.ts +0 -2
  76. package/lib/decorators/store.ts +0 -47
  77. package/lib/dsl/__test__/dsl-interpreter.test.ts +0 -453
  78. package/lib/dsl/__test__/parser.gen.test.ts +0 -296
  79. package/lib/dsl/dsl-interpreter.ts +0 -974
  80. package/lib/dsl/index.ts +0 -1
  81. package/lib/dsl/parser.gen.js +0 -1479
  82. package/lib/dsl/parser.pegjs +0 -130
  83. package/lib/dsl-descriptor.ts +0 -241
  84. package/lib/index.ts +0 -5
  85. package/lib/nl/__test__/context-aware-prompt.test.ts +0 -372
  86. package/lib/nl/__test__/context-selector.test.ts +0 -27
  87. package/lib/nl/__test__/nl-parser-groq-transport.test.ts +0 -139
  88. package/lib/nl/__test__/nl-parser-openai-parity.test.ts +0 -381
  89. package/lib/nl/__test__/nl-parser-openai-sampling.test.ts +0 -73
  90. package/lib/nl/__test__/nl-parser-openai-transport.test.ts +0 -79
  91. package/lib/nl/__test__/nl-parser-utils.test.ts +0 -98
  92. package/lib/nl/__test__/nl-parser.test.ts +0 -119
  93. package/lib/nl/__test__/parameter-tuning.test.ts +0 -137
  94. package/lib/nl/__test__/semantic-parsing-experiment.test.ts +0 -260
  95. package/lib/nl/context-selector.ts +0 -123
  96. package/lib/nl/index.ts +0 -19
  97. package/lib/nl/llm-monitoring.test.ts +0 -136
  98. package/lib/nl/llm-monitoring.ts +0 -339
  99. package/lib/nl/nl-parser-groq.ts +0 -510
  100. package/lib/nl/nl-parser-utils.ts +0 -310
  101. package/lib/nl/nl-parser.ts +0 -616
  102. package/lib/nl/prompt-gen.ts +0 -607
  103. package/lib/nl/prompt-res.ts +0 -207
  104. package/lib/nl-interpreter.ts +0 -262
@@ -1,616 +0,0 @@
1
- import { AgentResponsePayload, PromptGen } from "./prompt-gen";
2
- import { getLLMMonitor, type LLMMonitor } from "./llm-monitoring";
3
- import {
4
- buildCommandRepairInputText,
5
- buildCommandRepairMessages,
6
- buildFallbackAgentResponse,
7
- CommandValidator,
8
- createJsonChatCompletion,
9
- DEFAULT_SEMANTIC_PARSER_SAMPLING,
10
- extractResponseOutputText,
11
- extractStructuredAgentResponse,
12
- extractStructuredCommand,
13
- getStructuredAgentResponseFormat,
14
- getStructuredResponseFormat,
15
- getStructuredResponseTextFormat,
16
- normalizeAgentResponse,
17
- normalizeBaseUrl,
18
- SamplingParams,
19
- } from "./nl-parser-utils";
20
- export type { SamplingParams } from "./nl-parser-utils";
21
-
22
- export interface OpenAIParserOptions {
23
- statefulValidationRetry?: boolean;
24
- }
25
-
26
- function buildOpenAIEndpoint(baseUrl: string, suffix: string): string {
27
- const normalized = normalizeBaseUrl(baseUrl);
28
- if (normalized.endsWith(`/v1/${suffix}`)) {
29
- return normalized;
30
- }
31
- if (normalized.endsWith("/v1")) {
32
- return `${normalized}/${suffix}`;
33
- }
34
- return `${normalized}/v1/${suffix}`;
35
- }
36
-
37
- function requireConfiguredModel(model: string): string {
38
- const trimmed = model.trim();
39
- if (!trimmed) {
40
- throw new Error(
41
- "OpenAI parser requires semanticModel / SEMANTIC_MODEL configuration."
42
- );
43
- }
44
- return trimmed;
45
- }
46
-
47
- function asFiniteNumber(value: unknown): number | undefined {
48
- return typeof value === "number" && Number.isFinite(value) ? value : undefined;
49
- }
50
-
51
- function extractCostMetrics(response: any): {
52
- inputCost?: number;
53
- outputCost?: number;
54
- totalCost?: number;
55
- } {
56
- const usage = response?.usage ?? response?.usage_metadata ?? {};
57
- const inputCost =
58
- asFiniteNumber(usage?.input_cost) ??
59
- asFiniteNumber(usage?.prompt_cost) ??
60
- asFiniteNumber(usage?.prompt_cost_usd);
61
- const outputCost =
62
- asFiniteNumber(usage?.output_cost) ??
63
- asFiniteNumber(usage?.completion_cost) ??
64
- asFiniteNumber(usage?.completion_cost_usd);
65
- const totalCost =
66
- asFiniteNumber(usage?.total_cost) ??
67
- asFiniteNumber(usage?.cost) ??
68
- asFiniteNumber(usage?.total_cost_usd) ??
69
- (typeof inputCost === "number" && typeof outputCost === "number"
70
- ? inputCost + outputCost
71
- : undefined);
72
-
73
- return {
74
- ...(typeof inputCost === "number" ? { inputCost } : {}),
75
- ...(typeof outputCost === "number" ? { outputCost } : {}),
76
- ...(typeof totalCost === "number" ? { totalCost } : {}),
77
- };
78
- }
79
-
80
- export class NlParser {
81
- private chatCompletionsEndpoint: string;
82
- private responsesEndpoint: string;
83
- private samplingParams: Required<SamplingParams>;
84
- private llmMonitor!: LLMMonitor;
85
- private statefulValidationRetry: boolean;
86
-
87
- constructor(
88
- public prompt: PromptGen,
89
- private apiKey: string,
90
- private basePath: string,
91
- private model: string,
92
- samplingParams?: SamplingParams,
93
- langsmithApiKey?: string,
94
- langsmithProject?: string,
95
- langsmithEndpoint?: string,
96
- private validateCommand?: CommandValidator,
97
- parserOptions?: OpenAIParserOptions
98
- ) {
99
- this.model = requireConfiguredModel(model);
100
- this.chatCompletionsEndpoint = buildOpenAIEndpoint(this.basePath, "chat/completions");
101
- this.responsesEndpoint = buildOpenAIEndpoint(this.basePath, "responses");
102
- this.samplingParams = {
103
- ...DEFAULT_SEMANTIC_PARSER_SAMPLING,
104
- ...samplingParams,
105
- };
106
- this.statefulValidationRetry = !!parserOptions?.statefulValidationRetry;
107
-
108
- this.llmMonitor = getLLMMonitor({
109
- apiKey: langsmithApiKey,
110
- project: langsmithProject,
111
- endpoint: langsmithEndpoint,
112
- });
113
- }
114
-
115
- private async createChatCompletion(body: Record<string, unknown>): Promise<any> {
116
- return createJsonChatCompletion({
117
- endpoint: this.chatCompletionsEndpoint,
118
- apiKey: this.apiKey,
119
- body,
120
- provider: "openai",
121
- requestLabel: "OpenAI-compatible request failed",
122
- invalidPayloadLabel: "OpenAI-compatible endpoint returned an invalid JSON payload",
123
- });
124
- }
125
-
126
- private async createResponse(body: Record<string, unknown>): Promise<any> {
127
- return createJsonChatCompletion({
128
- endpoint: this.responsesEndpoint,
129
- apiKey: this.apiKey,
130
- body,
131
- provider: "openai",
132
- requestLabel: "OpenAI Responses request failed",
133
- invalidPayloadLabel: "OpenAI Responses endpoint returned an invalid JSON payload",
134
- });
135
- }
136
-
137
- private async runStructuredCommand(
138
- traceName: string,
139
- promptVariants: Array<{ label: string; text: string }>,
140
- userUtterance: string
141
- ): Promise<{
142
- response: any;
143
- completion: string;
144
- usedPromptText: string;
145
- responseId?: string;
146
- }> {
147
- let usedPromptText = promptVariants[0]?.text || "";
148
- let lastError: unknown;
149
-
150
- const runParse = async (
151
- strict: boolean,
152
- promptText: string
153
- ): Promise<{ response: any; command: string; responseId?: string }> => {
154
- if (this.statefulValidationRetry) {
155
- return this.runStructuredCommandResponse(promptText, strict);
156
- }
157
- return this.runStructuredCommandMessages([
158
- {
159
- role: "user",
160
- content: promptText,
161
- },
162
- ], strict);
163
- };
164
-
165
- for (const promptVariant of promptVariants) {
166
- usedPromptText = promptVariant.text;
167
- try {
168
- const result = await this.llmMonitor.traceCall(
169
- `${traceName}_${promptVariant.label}`,
170
- { model: this.model, provider: "openai" },
171
- async () => runParse(false, promptVariant.text)
172
- );
173
- const validated = await this.validateOrRepairCommand(
174
- traceName,
175
- promptVariant.label,
176
- userUtterance,
177
- result.command,
178
- usedPromptText,
179
- result.responseId
180
- );
181
- return {
182
- response: result.response,
183
- completion: validated,
184
- usedPromptText,
185
- ...(result.responseId ? { responseId: result.responseId } : {}),
186
- };
187
- } catch {
188
- try {
189
- const result = await this.llmMonitor.traceCall(
190
- `${traceName}_${promptVariant.label}`,
191
- { model: this.model, provider: "openai" },
192
- async () => runParse(true, promptVariant.text)
193
- );
194
- const validated = await this.validateOrRepairCommand(
195
- traceName,
196
- promptVariant.label,
197
- userUtterance,
198
- result.command,
199
- usedPromptText,
200
- result.responseId
201
- );
202
- return {
203
- response: result.response,
204
- completion: validated,
205
- usedPromptText,
206
- ...(result.responseId ? { responseId: result.responseId } : {}),
207
- };
208
- } catch (strictError) {
209
- lastError = strictError;
210
- }
211
- }
212
- }
213
-
214
- throw (
215
- lastError instanceof Error
216
- ? lastError
217
- : new Error("OpenAI parse failed for filtered prompt")
218
- );
219
- }
220
-
221
- private async runStructuredCommandResponse(
222
- promptText: string,
223
- strict: boolean
224
- ): Promise<{ response: any; command: string; responseId?: string }> {
225
- const response = await this.createResponse({
226
- model: this.model,
227
- store: true,
228
- input: [
229
- {
230
- role: "user",
231
- content: [{ type: "input_text", text: promptText }],
232
- },
233
- ],
234
- text: {
235
- format: getStructuredResponseTextFormat(strict),
236
- },
237
- });
238
-
239
- const raw = extractResponseOutputText(response);
240
- const command = extractStructuredCommand(raw);
241
- if (!command) {
242
- throw new Error("OpenAI Responses structured output missing command");
243
- }
244
-
245
- return {
246
- response,
247
- command,
248
- ...(typeof response?.id === "string" ? { responseId: response.id } : {}),
249
- };
250
- }
251
-
252
- private async runStructuredCommandMessages(
253
- messages: ReadonlyArray<{
254
- role: "system" | "user" | "assistant";
255
- content: string;
256
- }>,
257
- strict: boolean
258
- ): Promise<{ response: any; command: string }> {
259
- const response = await this.createChatCompletion({
260
- model: this.model,
261
- messages: [...messages],
262
- temperature: this.samplingParams.temperature,
263
- max_tokens: 256,
264
- top_p: this.samplingParams.top_p,
265
- frequency_penalty: this.samplingParams.frequency_penalty,
266
- presence_penalty: this.samplingParams.presence_penalty,
267
- response_format: getStructuredResponseFormat(strict),
268
- });
269
-
270
- const raw = response.choices[0]?.message?.content ?? null;
271
- const command = extractStructuredCommand(raw);
272
- if (!command) {
273
- throw new Error("OpenAI structured output missing command");
274
- }
275
- return { response, command };
276
- }
277
-
278
- private async validateOrRepairCommand(
279
- traceName: string,
280
- promptLabel: string,
281
- userUtterance: string,
282
- command: string,
283
- lastUsedPrompt?: string,
284
- responseId?: string
285
- ): Promise<string> {
286
- if (!this.validateCommand) {
287
- return command;
288
- }
289
-
290
- try {
291
- await this.validateCommand(command);
292
- return command;
293
- } catch (validationError) {
294
- const validationMessage =
295
- validationError instanceof Error ? validationError.message : String(validationError);
296
-
297
- if (this.statefulValidationRetry && responseId) {
298
- let lastError: unknown = validationError;
299
- for (const strict of [false, true]) {
300
- try {
301
- const repaired = await this.llmMonitor.traceCall(
302
- `${traceName}_${promptLabel}_repair_${strict ? "strict" : "lenient"}`,
303
- { model: this.model, provider: "openai" },
304
- async () =>
305
- this.runStructuredCommandRepairResponse(
306
- responseId,
307
- buildCommandRepairInputText(userUtterance, command, validationMessage),
308
- strict
309
- )
310
- );
311
- await this.validateCommand(repaired.command);
312
- return repaired.command;
313
- } catch (repairError) {
314
- lastError = repairError;
315
- }
316
- }
317
-
318
- throw (
319
- lastError instanceof Error
320
- ? lastError
321
- : new Error("OpenAI repair failed after semantic validation")
322
- );
323
- }
324
-
325
- const messages = buildCommandRepairMessages(
326
- userUtterance,
327
- command,
328
- validationMessage,
329
- lastUsedPrompt
330
- );
331
-
332
- let lastError: unknown = validationError;
333
- for (const strict of [false, true]) {
334
- try {
335
- const repaired = await this.llmMonitor.traceCall(
336
- `${traceName}_${promptLabel}_repair_${strict ? "strict" : "lenient"}`,
337
- { model: this.model, provider: "openai" },
338
- async () => this.runStructuredCommandMessages(messages, strict)
339
- );
340
- await this.validateCommand(repaired.command);
341
- return repaired.command;
342
- } catch (repairError) {
343
- lastError = repairError;
344
- }
345
- }
346
-
347
- throw (
348
- lastError instanceof Error
349
- ? lastError
350
- : new Error("OpenAI repair failed after semantic validation")
351
- );
352
- }
353
- }
354
-
355
- private async runStructuredCommandRepairResponse(
356
- previousResponseId: string,
357
- repairText: string,
358
- strict: boolean
359
- ): Promise<{ response: any; command: string; responseId?: string }> {
360
- const response = await this.createResponse({
361
- model: this.model,
362
- store: true,
363
- previous_response_id: previousResponseId,
364
- input: [
365
- {
366
- role: "user",
367
- content: [{ type: "input_text", text: repairText }],
368
- },
369
- ],
370
- text: {
371
- format: getStructuredResponseTextFormat(strict),
372
- },
373
- });
374
-
375
- const raw = extractResponseOutputText(response);
376
- const command = extractStructuredCommand(raw);
377
- if (!command) {
378
- throw new Error("OpenAI Responses repair output missing command");
379
- }
380
-
381
- return {
382
- response,
383
- command,
384
- ...(typeof response?.id === "string" ? { responseId: response.id } : {}),
385
- };
386
- }
387
-
388
- /** @deprecated Prefer parse() for all new code paths. */
389
- async oldParse(nl: string): Promise<string | null> {
390
- const promptText = this.prompt.prompt(nl);
391
- const startTime = Date.now();
392
-
393
- try {
394
- const result = await this.runStructuredCommand("oldParse", [
395
- { label: "legacy_prompt", text: promptText },
396
- ], nl);
397
-
398
- await this.llmMonitor.logCall({
399
- model: this.model,
400
- provider: "openai",
401
- promptTokens: result.response.usage?.prompt_tokens,
402
- completionTokens: result.response.usage?.completion_tokens,
403
- totalTokens: result.response.usage?.total_tokens,
404
- ...extractCostMetrics(result.response),
405
- prompt: result.usedPromptText,
406
- completion: result.completion,
407
- latency: Date.now() - startTime,
408
- timestamp: new Date(),
409
- });
410
-
411
- return result.completion;
412
- } catch (error) {
413
- await this.llmMonitor.logCall({
414
- model: this.model,
415
- provider: "openai",
416
- prompt: promptText,
417
- completion: null,
418
- latency: Date.now() - startTime,
419
- timestamp: new Date(),
420
- error: error instanceof Error ? error.message : String(error),
421
- });
422
- throw error;
423
- }
424
- }
425
-
426
- async parse(nl: string): Promise<string | null> {
427
- const startTime = Date.now();
428
- const promptVariants: Array<{ label: string; text: string }> = [];
429
- const defaultPrompt = this.prompt.zero_shot_prompt(nl);
430
- promptVariants.push({ label: "filtered", text: defaultPrompt });
431
-
432
- try {
433
- const result = await this.runStructuredCommand("parse", promptVariants, nl);
434
- await this.llmMonitor.logCall({
435
- model: this.model,
436
- provider: "openai",
437
- promptTokens: result.response.usage?.prompt_tokens,
438
- completionTokens: result.response.usage?.completion_tokens,
439
- totalTokens: result.response.usage?.total_tokens,
440
- ...extractCostMetrics(result.response),
441
- prompt: result.usedPromptText,
442
- completion: result.completion,
443
- latency: Date.now() - startTime,
444
- timestamp: new Date(),
445
- });
446
- return result.completion;
447
- } catch (error) {
448
- await this.llmMonitor.logCall({
449
- model: this.model,
450
- provider: "openai",
451
- prompt: promptVariants[0]?.text || "",
452
- completion: null,
453
- latency: Date.now() - startTime,
454
- timestamp: new Date(),
455
- error: error instanceof Error ? error.message : String(error),
456
- });
457
- throw error;
458
- }
459
- }
460
-
461
- async parseGpt4(nl: string): Promise<string | null> {
462
- const promptText = this.prompt.prompt(nl);
463
- const startTime = Date.now();
464
-
465
- const runParse = async (strict: boolean) => {
466
- return this.runStructuredCommandMessages(
467
- [
468
- {
469
- role: "system",
470
- content: "only generate one line of code",
471
- },
472
- {
473
- role: "user",
474
- content: promptText,
475
- },
476
- ],
477
- strict
478
- );
479
- };
480
-
481
- try {
482
- let result;
483
- try {
484
- result = await this.llmMonitor.traceCall(
485
- "parseGpt4",
486
- { model: this.model, provider: "openai" },
487
- async () => runParse(false)
488
- );
489
- } catch {
490
- result = await this.llmMonitor.traceCall(
491
- "parseGpt4",
492
- { model: this.model, provider: "openai" },
493
- async () => runParse(true)
494
- );
495
- }
496
-
497
- const validatedCommand = await this.validateOrRepairCommand(
498
- "parseGpt4",
499
- "legacy_prompt",
500
- nl,
501
- result.command,
502
- promptText
503
- );
504
-
505
- await this.llmMonitor.logCall({
506
- model: this.model,
507
- provider: "openai",
508
- promptTokens: result.response.usage?.prompt_tokens,
509
- completionTokens: result.response.usage?.completion_tokens,
510
- totalTokens: result.response.usage?.total_tokens,
511
- ...extractCostMetrics(result.response),
512
- prompt: promptText,
513
- completion: validatedCommand,
514
- latency: Date.now() - startTime,
515
- timestamp: new Date(),
516
- });
517
-
518
- return validatedCommand;
519
- } catch (error) {
520
- await this.llmMonitor.logCall({
521
- model: this.model,
522
- provider: "openai",
523
- prompt: promptText,
524
- completion: null,
525
- latency: Date.now() - startTime,
526
- timestamp: new Date(),
527
- error: error instanceof Error ? error.message : String(error),
528
- });
529
- throw error;
530
- }
531
- }
532
-
533
- async respond(
534
- nl: string,
535
- parsed: string,
536
- result: string
537
- ): Promise<AgentResponsePayload | null> {
538
- const promptText = this.prompt.response_prompt(nl, parsed, result);
539
- const startTime = Date.now();
540
-
541
- const runRespond = async (strict: boolean) => {
542
- const response = await this.createChatCompletion({
543
- model: this.model,
544
- messages: [
545
- {
546
- role: "system",
547
- content: "Return only a strict JSON object for agent_response_json.",
548
- },
549
- {
550
- role: "user",
551
- content: promptText,
552
- },
553
- ],
554
- temperature: this.samplingParams.temperature,
555
- max_tokens: 256,
556
- top_p: this.samplingParams.top_p,
557
- frequency_penalty: this.samplingParams.frequency_penalty,
558
- presence_penalty: this.samplingParams.presence_penalty,
559
- response_format: getStructuredAgentResponseFormat(strict),
560
- });
561
-
562
- const raw = response.choices[0]?.message?.content ?? null;
563
- const payload = extractStructuredAgentResponse(raw);
564
- if (!payload) {
565
- throw new Error("OpenAI structured output missing agent response payload");
566
- }
567
- return { response, payload };
568
- };
569
-
570
- try {
571
- let result;
572
- try {
573
- result = await this.llmMonitor.traceCall(
574
- "respond",
575
- { model: this.model, provider: "openai" },
576
- async () => runRespond(false)
577
- );
578
- } catch {
579
- result = await this.llmMonitor.traceCall(
580
- "respond",
581
- { model: this.model, provider: "openai" },
582
- async () => runRespond(true)
583
- );
584
- }
585
-
586
- const normalizedCompletion = normalizeAgentResponse(result.payload);
587
-
588
- await this.llmMonitor.logCall({
589
- model: this.model,
590
- provider: "openai",
591
- promptTokens: result.response.usage?.prompt_tokens,
592
- completionTokens: result.response.usage?.completion_tokens,
593
- totalTokens: result.response.usage?.total_tokens,
594
- ...extractCostMetrics(result.response),
595
- prompt: promptText,
596
- completion: JSON.stringify(normalizedCompletion),
597
- latency: Date.now() - startTime,
598
- timestamp: new Date(),
599
- });
600
-
601
- return normalizedCompletion;
602
- } catch (error) {
603
- const fallbackResponse = buildFallbackAgentResponse(parsed, result);
604
- await this.llmMonitor.logCall({
605
- model: this.model,
606
- provider: "openai",
607
- prompt: promptText,
608
- completion: JSON.stringify(fallbackResponse),
609
- latency: Date.now() - startTime,
610
- timestamp: new Date(),
611
- error: error instanceof Error ? error.message : String(error),
612
- });
613
- return fallbackResponse;
614
- }
615
- }
616
- }