@garrix82/reactgenie-dsl 1.0.0
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.
- package/README.md +436 -0
- package/dist/__test__/dsl-descriptor.test.d.ts +1 -0
- package/dist/__test__/dsl-descriptor.test.js +27 -0
- package/dist/__test__/dsl-descriptor.test.js.map +1 -0
- package/dist/__test__/example_descriptor.d.ts +125 -0
- package/dist/__test__/example_descriptor.js +607 -0
- package/dist/__test__/example_descriptor.js.map +1 -0
- package/dist/__test__/food_descriptor.state.json +1 -0
- package/dist/__test__/food_descriptor.test.d.ts +74 -0
- package/dist/__test__/food_descriptor.test.js +205 -0
- package/dist/__test__/food_descriptor.test.js.map +1 -0
- package/dist/__test__/nl-interpreter-provider-selection.test.d.ts +1 -0
- package/dist/__test__/nl-interpreter-provider-selection.test.js +73 -0
- package/dist/__test__/nl-interpreter-provider-selection.test.js.map +1 -0
- package/dist/__test__/nl-interpreter.test.d.ts +1 -0
- package/dist/__test__/nl-interpreter.test.js +86 -0
- package/dist/__test__/nl-interpreter.test.js.map +1 -0
- package/dist/decorators/__test__/decorators.test.d.ts +1 -0
- package/dist/decorators/__test__/decorators.test.js +182 -0
- package/dist/decorators/__test__/decorators.test.js.map +1 -0
- package/dist/decorators/__test__/inheritance-descriptor.test.d.ts +1 -0
- package/dist/decorators/__test__/inheritance-descriptor.test.js +107 -0
- package/dist/decorators/__test__/inheritance-descriptor.test.js.map +1 -0
- package/dist/decorators/decorators.d.ts +20 -0
- package/dist/decorators/decorators.js +520 -0
- package/dist/decorators/decorators.js.map +1 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.js +19 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/store.d.ts +12 -0
- package/dist/decorators/store.js +43 -0
- package/dist/decorators/store.js.map +1 -0
- package/dist/dsl/__test__/dsl-interpreter.test.d.ts +1 -0
- package/dist/dsl/__test__/dsl-interpreter.test.js +334 -0
- package/dist/dsl/__test__/dsl-interpreter.test.js.map +1 -0
- package/dist/dsl/__test__/parser.gen.test.d.ts +1 -0
- package/dist/dsl/__test__/parser.gen.test.js +283 -0
- package/dist/dsl/__test__/parser.gen.test.js.map +1 -0
- package/dist/dsl/dsl-interpreter.d.ts +66 -0
- package/dist/dsl/dsl-interpreter.js +767 -0
- package/dist/dsl/dsl-interpreter.js.map +1 -0
- package/dist/dsl/index.d.ts +1 -0
- package/dist/dsl/index.js +18 -0
- package/dist/dsl/index.js.map +1 -0
- package/dist/dsl/parser.gen.d.ts +10 -0
- package/dist/dsl/parser.gen.js +1524 -0
- package/dist/dsl/parser.gen.js.map +1 -0
- package/dist/dsl-descriptor.d.ts +75 -0
- package/dist/dsl-descriptor.js +151 -0
- package/dist/dsl-descriptor.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/nl/__test__/context-aware-prompt.test.d.ts +1 -0
- package/dist/nl/__test__/context-aware-prompt.test.js +247 -0
- package/dist/nl/__test__/context-aware-prompt.test.js.map +1 -0
- package/dist/nl/__test__/context-selector.test.d.ts +1 -0
- package/dist/nl/__test__/context-selector.test.js +20 -0
- package/dist/nl/__test__/context-selector.test.js.map +1 -0
- package/dist/nl/__test__/nl-parser-groq-transport.test.d.ts +1 -0
- package/dist/nl/__test__/nl-parser-groq-transport.test.js +87 -0
- package/dist/nl/__test__/nl-parser-groq-transport.test.js.map +1 -0
- package/dist/nl/__test__/nl-parser-openai-parity.test.d.ts +1 -0
- package/dist/nl/__test__/nl-parser-openai-parity.test.js +206 -0
- package/dist/nl/__test__/nl-parser-openai-parity.test.js.map +1 -0
- package/dist/nl/__test__/nl-parser-openai-sampling.test.d.ts +1 -0
- package/dist/nl/__test__/nl-parser-openai-sampling.test.js +44 -0
- package/dist/nl/__test__/nl-parser-openai-sampling.test.js.map +1 -0
- package/dist/nl/__test__/nl-parser-openai-transport.test.d.ts +1 -0
- package/dist/nl/__test__/nl-parser-openai-transport.test.js +55 -0
- package/dist/nl/__test__/nl-parser-openai-transport.test.js.map +1 -0
- package/dist/nl/__test__/nl-parser-utils.test.d.ts +1 -0
- package/dist/nl/__test__/nl-parser-utils.test.js +70 -0
- package/dist/nl/__test__/nl-parser-utils.test.js.map +1 -0
- package/dist/nl/__test__/nl-parser.test.d.ts +1 -0
- package/dist/nl/__test__/nl-parser.test.js +64 -0
- package/dist/nl/__test__/nl-parser.test.js.map +1 -0
- package/dist/nl/__test__/parameter-tuning.test.d.ts +1 -0
- package/dist/nl/__test__/parameter-tuning.test.js +95 -0
- package/dist/nl/__test__/parameter-tuning.test.js.map +1 -0
- package/dist/nl/__test__/semantic-parsing-experiment.test.d.ts +1 -0
- package/dist/nl/__test__/semantic-parsing-experiment.test.js +178 -0
- package/dist/nl/__test__/semantic-parsing-experiment.test.js.map +1 -0
- package/dist/nl/context-selector.d.ts +17 -0
- package/dist/nl/context-selector.js +99 -0
- package/dist/nl/context-selector.js.map +1 -0
- package/dist/nl/index.d.ts +6 -0
- package/dist/nl/index.js +15 -0
- package/dist/nl/index.js.map +1 -0
- package/dist/nl/llm-monitoring.d.ts +49 -0
- package/dist/nl/llm-monitoring.js +243 -0
- package/dist/nl/llm-monitoring.js.map +1 -0
- package/dist/nl/llm-monitoring.test.d.ts +5 -0
- package/dist/nl/llm-monitoring.test.js +101 -0
- package/dist/nl/llm-monitoring.test.js.map +1 -0
- package/dist/nl/nl-parser-groq.d.ts +21 -0
- package/dist/nl/nl-parser-groq.js +357 -0
- package/dist/nl/nl-parser-groq.js.map +1 -0
- package/dist/nl/nl-parser-utils.d.ts +101 -0
- package/dist/nl/nl-parser-utils.js +255 -0
- package/dist/nl/nl-parser-utils.js.map +1 -0
- package/dist/nl/nl-parser.d.ts +30 -0
- package/dist/nl/nl-parser.js +433 -0
- package/dist/nl/nl-parser.js.map +1 -0
- package/dist/nl/prompt-gen.d.ts +93 -0
- package/dist/nl/prompt-gen.js +369 -0
- package/dist/nl/prompt-gen.js.map +1 -0
- package/dist/nl/prompt-res.d.ts +16 -0
- package/dist/nl/prompt-res.js +190 -0
- package/dist/nl/prompt-res.js.map +1 -0
- package/dist/nl-interpreter.d.ts +48 -0
- package/dist/nl-interpreter.js +155 -0
- package/dist/nl-interpreter.js.map +1 -0
- package/lib/__test__/dsl-descriptor.test.ts +27 -0
- package/lib/__test__/example_descriptor.ts +762 -0
- package/lib/__test__/food_descriptor.state.json +1 -0
- package/lib/__test__/food_descriptor.test.ts +331 -0
- package/lib/__test__/nl-interpreter-provider-selection.test.ts +126 -0
- package/lib/__test__/nl-interpreter.test.ts +129 -0
- package/lib/decorators/__test__/decorators.test.ts +177 -0
- package/lib/decorators/__test__/inheritance-descriptor.test.ts +92 -0
- package/lib/decorators/decorators.ts +754 -0
- package/lib/decorators/index.ts +2 -0
- package/lib/decorators/store.ts +47 -0
- package/lib/dsl/__test__/dsl-interpreter.test.ts +453 -0
- package/lib/dsl/__test__/parser.gen.test.ts +296 -0
- package/lib/dsl/dsl-interpreter.ts +974 -0
- package/lib/dsl/index.ts +1 -0
- package/lib/dsl/parser.gen.js +1479 -0
- package/lib/dsl/parser.pegjs +130 -0
- package/lib/dsl-descriptor.ts +241 -0
- package/lib/index.ts +5 -0
- package/lib/nl/__test__/context-aware-prompt.test.ts +372 -0
- package/lib/nl/__test__/context-selector.test.ts +27 -0
- package/lib/nl/__test__/nl-parser-groq-transport.test.ts +139 -0
- package/lib/nl/__test__/nl-parser-openai-parity.test.ts +381 -0
- package/lib/nl/__test__/nl-parser-openai-sampling.test.ts +73 -0
- package/lib/nl/__test__/nl-parser-openai-transport.test.ts +79 -0
- package/lib/nl/__test__/nl-parser-utils.test.ts +98 -0
- package/lib/nl/__test__/nl-parser.test.ts +119 -0
- package/lib/nl/__test__/parameter-tuning.test.ts +137 -0
- package/lib/nl/__test__/semantic-parsing-experiment.test.ts +260 -0
- package/lib/nl/context-selector.ts +123 -0
- package/lib/nl/index.ts +19 -0
- package/lib/nl/llm-monitoring.test.ts +136 -0
- package/lib/nl/llm-monitoring.ts +339 -0
- package/lib/nl/nl-parser-groq.ts +510 -0
- package/lib/nl/nl-parser-utils.ts +310 -0
- package/lib/nl/nl-parser.ts +616 -0
- package/lib/nl/prompt-gen.ts +607 -0
- package/lib/nl/prompt-res.ts +207 -0
- package/lib/nl-interpreter.ts +262 -0
- package/package.json +58 -0
|
@@ -0,0 +1,616 @@
|
|
|
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
|
+
}
|