@garrix82/reactgenie-dsl 1.0.0 → 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 (105) hide show
  1. package/.env +10 -0
  2. package/.env.example +17 -0
  3. package/.github/workflows/publish.yml +20 -0
  4. package/README.md +5 -1
  5. package/package.json +1 -5
  6. package/dist/__test__/dsl-descriptor.test.d.ts +0 -1
  7. package/dist/__test__/dsl-descriptor.test.js +0 -27
  8. package/dist/__test__/dsl-descriptor.test.js.map +0 -1
  9. package/dist/__test__/example_descriptor.d.ts +0 -125
  10. package/dist/__test__/example_descriptor.js +0 -607
  11. package/dist/__test__/example_descriptor.js.map +0 -1
  12. package/dist/__test__/food_descriptor.state.json +0 -1
  13. package/dist/__test__/food_descriptor.test.d.ts +0 -74
  14. package/dist/__test__/food_descriptor.test.js +0 -205
  15. package/dist/__test__/food_descriptor.test.js.map +0 -1
  16. package/dist/__test__/nl-interpreter-provider-selection.test.d.ts +0 -1
  17. package/dist/__test__/nl-interpreter-provider-selection.test.js +0 -73
  18. package/dist/__test__/nl-interpreter-provider-selection.test.js.map +0 -1
  19. package/dist/__test__/nl-interpreter.test.d.ts +0 -1
  20. package/dist/__test__/nl-interpreter.test.js +0 -86
  21. package/dist/__test__/nl-interpreter.test.js.map +0 -1
  22. package/dist/decorators/__test__/decorators.test.d.ts +0 -1
  23. package/dist/decorators/__test__/decorators.test.js +0 -182
  24. package/dist/decorators/__test__/decorators.test.js.map +0 -1
  25. package/dist/decorators/__test__/inheritance-descriptor.test.d.ts +0 -1
  26. package/dist/decorators/__test__/inheritance-descriptor.test.js +0 -107
  27. package/dist/decorators/__test__/inheritance-descriptor.test.js.map +0 -1
  28. package/dist/dsl/__test__/dsl-interpreter.test.d.ts +0 -1
  29. package/dist/dsl/__test__/dsl-interpreter.test.js +0 -334
  30. package/dist/dsl/__test__/dsl-interpreter.test.js.map +0 -1
  31. package/dist/dsl/__test__/parser.gen.test.d.ts +0 -1
  32. package/dist/dsl/__test__/parser.gen.test.js +0 -283
  33. package/dist/dsl/__test__/parser.gen.test.js.map +0 -1
  34. package/dist/nl/__test__/context-aware-prompt.test.d.ts +0 -1
  35. package/dist/nl/__test__/context-aware-prompt.test.js +0 -247
  36. package/dist/nl/__test__/context-aware-prompt.test.js.map +0 -1
  37. package/dist/nl/__test__/context-selector.test.d.ts +0 -1
  38. package/dist/nl/__test__/context-selector.test.js +0 -20
  39. package/dist/nl/__test__/context-selector.test.js.map +0 -1
  40. package/dist/nl/__test__/nl-parser-groq-transport.test.d.ts +0 -1
  41. package/dist/nl/__test__/nl-parser-groq-transport.test.js +0 -87
  42. package/dist/nl/__test__/nl-parser-groq-transport.test.js.map +0 -1
  43. package/dist/nl/__test__/nl-parser-openai-parity.test.d.ts +0 -1
  44. package/dist/nl/__test__/nl-parser-openai-parity.test.js +0 -206
  45. package/dist/nl/__test__/nl-parser-openai-parity.test.js.map +0 -1
  46. package/dist/nl/__test__/nl-parser-openai-sampling.test.d.ts +0 -1
  47. package/dist/nl/__test__/nl-parser-openai-sampling.test.js +0 -44
  48. package/dist/nl/__test__/nl-parser-openai-sampling.test.js.map +0 -1
  49. package/dist/nl/__test__/nl-parser-openai-transport.test.d.ts +0 -1
  50. package/dist/nl/__test__/nl-parser-openai-transport.test.js +0 -55
  51. package/dist/nl/__test__/nl-parser-openai-transport.test.js.map +0 -1
  52. package/dist/nl/__test__/nl-parser-utils.test.d.ts +0 -1
  53. package/dist/nl/__test__/nl-parser-utils.test.js +0 -70
  54. package/dist/nl/__test__/nl-parser-utils.test.js.map +0 -1
  55. package/dist/nl/__test__/nl-parser.test.d.ts +0 -1
  56. package/dist/nl/__test__/nl-parser.test.js +0 -64
  57. package/dist/nl/__test__/nl-parser.test.js.map +0 -1
  58. package/dist/nl/__test__/parameter-tuning.test.d.ts +0 -1
  59. package/dist/nl/__test__/parameter-tuning.test.js +0 -95
  60. package/dist/nl/__test__/parameter-tuning.test.js.map +0 -1
  61. package/dist/nl/__test__/semantic-parsing-experiment.test.d.ts +0 -1
  62. package/dist/nl/__test__/semantic-parsing-experiment.test.js +0 -178
  63. package/dist/nl/__test__/semantic-parsing-experiment.test.js.map +0 -1
  64. package/dist/nl/llm-monitoring.test.d.ts +0 -5
  65. package/dist/nl/llm-monitoring.test.js +0 -101
  66. package/dist/nl/llm-monitoring.test.js.map +0 -1
  67. package/lib/__test__/dsl-descriptor.test.ts +0 -27
  68. package/lib/__test__/example_descriptor.ts +0 -762
  69. package/lib/__test__/food_descriptor.state.json +0 -1
  70. package/lib/__test__/food_descriptor.test.ts +0 -331
  71. package/lib/__test__/nl-interpreter-provider-selection.test.ts +0 -126
  72. package/lib/__test__/nl-interpreter.test.ts +0 -129
  73. package/lib/decorators/__test__/decorators.test.ts +0 -177
  74. package/lib/decorators/__test__/inheritance-descriptor.test.ts +0 -92
  75. package/lib/decorators/decorators.ts +0 -754
  76. package/lib/decorators/index.ts +0 -2
  77. package/lib/decorators/store.ts +0 -47
  78. package/lib/dsl/__test__/dsl-interpreter.test.ts +0 -453
  79. package/lib/dsl/__test__/parser.gen.test.ts +0 -296
  80. package/lib/dsl/dsl-interpreter.ts +0 -974
  81. package/lib/dsl/index.ts +0 -1
  82. package/lib/dsl/parser.gen.js +0 -1479
  83. package/lib/dsl/parser.pegjs +0 -130
  84. package/lib/dsl-descriptor.ts +0 -241
  85. package/lib/index.ts +0 -5
  86. package/lib/nl/__test__/context-aware-prompt.test.ts +0 -372
  87. package/lib/nl/__test__/context-selector.test.ts +0 -27
  88. package/lib/nl/__test__/nl-parser-groq-transport.test.ts +0 -139
  89. package/lib/nl/__test__/nl-parser-openai-parity.test.ts +0 -381
  90. package/lib/nl/__test__/nl-parser-openai-sampling.test.ts +0 -73
  91. package/lib/nl/__test__/nl-parser-openai-transport.test.ts +0 -79
  92. package/lib/nl/__test__/nl-parser-utils.test.ts +0 -98
  93. package/lib/nl/__test__/nl-parser.test.ts +0 -119
  94. package/lib/nl/__test__/parameter-tuning.test.ts +0 -137
  95. package/lib/nl/__test__/semantic-parsing-experiment.test.ts +0 -260
  96. package/lib/nl/context-selector.ts +0 -123
  97. package/lib/nl/index.ts +0 -19
  98. package/lib/nl/llm-monitoring.test.ts +0 -136
  99. package/lib/nl/llm-monitoring.ts +0 -339
  100. package/lib/nl/nl-parser-groq.ts +0 -510
  101. package/lib/nl/nl-parser-utils.ts +0 -310
  102. package/lib/nl/nl-parser.ts +0 -616
  103. package/lib/nl/prompt-gen.ts +0 -607
  104. package/lib/nl/prompt-res.ts +0 -207
  105. 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
- }