@haikeilabs/agentware 0.1.0 → 0.2.1
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 +19 -0
- package/dist/executor/agent_loop.d.ts +50 -0
- package/dist/executor/agent_loop.d.ts.map +1 -0
- package/dist/executor/agent_loop.js +255 -0
- package/dist/executor/executor.d.ts.map +1 -1
- package/dist/executor/executor.js +6 -2
- package/dist/executor/index.d.ts +2 -1
- package/dist/executor/index.d.ts.map +1 -1
- package/dist/executor/index.js +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/kei/index.d.ts +2 -0
- package/dist/kei/index.d.ts.map +1 -0
- package/dist/kei/index.js +1 -0
- package/dist/kei/runtimeLink.d.ts +281 -0
- package/dist/kei/runtimeLink.d.ts.map +1 -0
- package/dist/kei/runtimeLink.js +591 -0
- package/dist/llm/backend.d.ts +7 -0
- package/dist/llm/backend.d.ts.map +1 -1
- package/dist/llm/index.d.ts +2 -1
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +1 -0
- package/dist/llm/openai_backend.d.ts +51 -0
- package/dist/llm/openai_backend.d.ts.map +1 -0
- package/dist/llm/openai_backend.js +203 -0
- package/dist/llm/response.d.ts +5 -0
- package/dist/llm/response.d.ts.map +1 -1
- package/dist/middleware/guardrails/response_validator.d.ts +1 -1
- package/dist/middleware/guardrails/response_validator.d.ts.map +1 -1
- package/dist/middleware/guardrails/response_validator.js +1 -1
- package/dist/middleware/index.d.ts +5 -0
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +5 -0
- package/dist/middleware/inference.d.ts +8 -0
- package/dist/middleware/inference.d.ts.map +1 -1
- package/dist/middleware/inference.js +73 -31
- package/dist/reasoning/adapter.d.ts +33 -0
- package/dist/reasoning/adapter.d.ts.map +1 -0
- package/dist/reasoning/adapter.js +365 -0
- package/dist/reasoning/errors.d.ts +28 -0
- package/dist/reasoning/errors.d.ts.map +1 -0
- package/dist/reasoning/errors.js +57 -0
- package/dist/reasoning/index.d.ts +6 -0
- package/dist/reasoning/index.d.ts.map +1 -0
- package/dist/reasoning/index.js +4 -0
- package/dist/reasoning/options.d.ts +18 -0
- package/dist/reasoning/options.d.ts.map +1 -0
- package/dist/reasoning/options.js +18 -0
- package/dist/reasoning/types.d.ts +44 -0
- package/dist/reasoning/types.d.ts.map +1 -0
- package/dist/reasoning/types.js +32 -0
- package/dist/toolformat/formatter.d.ts +3 -3
- package/dist/toolformat/formatter.d.ts.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -2
- package/dist/tools/registry.d.ts +4 -4
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/tool.d.ts +8 -1
- package/dist/tools/tool.d.ts.map +1 -1
- package/dist/tools/tool.js +7 -0
- package/package.json +17 -1
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { Role } from "./request.js";
|
|
2
|
+
export class OpenAIError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
constructor(message, status) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "OpenAIError";
|
|
7
|
+
this.status = status;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
12
|
+
const DEFAULT_MAX_RESPONSE_BYTES = 1_048_576;
|
|
13
|
+
const DEFAULT_MAX_TOOL_ARG_BYTES = 65_536;
|
|
14
|
+
const DEFAULT_CONTEXT_WINDOW = 128_000;
|
|
15
|
+
const MAX_ERROR_DETAIL_CHARS = 512;
|
|
16
|
+
export function parseToolArguments(raw, maxBytes) {
|
|
17
|
+
if (!raw) {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
const bounded = raw.length > maxBytes ? raw.slice(0, maxBytes) : raw;
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(bounded);
|
|
23
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
24
|
+
return parsed;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// fall through to raw fallback
|
|
29
|
+
}
|
|
30
|
+
return { _raw: bounded };
|
|
31
|
+
}
|
|
32
|
+
function toOpenAIMessage(message) {
|
|
33
|
+
switch (message.role) {
|
|
34
|
+
case Role.TOOL:
|
|
35
|
+
return {
|
|
36
|
+
role: "tool",
|
|
37
|
+
tool_call_id: message.tool_call_id ?? "",
|
|
38
|
+
content: message.content,
|
|
39
|
+
};
|
|
40
|
+
case Role.ASSISTANT:
|
|
41
|
+
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
42
|
+
return {
|
|
43
|
+
role: "assistant",
|
|
44
|
+
content: message.content || null,
|
|
45
|
+
tool_calls: message.tool_calls.map((tc) => ({
|
|
46
|
+
id: tc.id,
|
|
47
|
+
type: "function",
|
|
48
|
+
function: { name: tc.name, arguments: JSON.stringify(tc.arguments) },
|
|
49
|
+
})),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return { role: "assistant", content: message.content };
|
|
53
|
+
default:
|
|
54
|
+
return { role: message.role, content: message.content };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function toOpenAITool(tool) {
|
|
58
|
+
return {
|
|
59
|
+
type: "function",
|
|
60
|
+
function: {
|
|
61
|
+
name: tool.name,
|
|
62
|
+
description: tool.description,
|
|
63
|
+
parameters: tool.input_schema,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function truncateDetail(text) {
|
|
68
|
+
if (text.length <= MAX_ERROR_DETAIL_CHARS) {
|
|
69
|
+
return text;
|
|
70
|
+
}
|
|
71
|
+
return text.slice(0, MAX_ERROR_DETAIL_CHARS) + "...";
|
|
72
|
+
}
|
|
73
|
+
function toError(err) {
|
|
74
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
75
|
+
}
|
|
76
|
+
export class OpenAIBackend {
|
|
77
|
+
model;
|
|
78
|
+
baseUrl;
|
|
79
|
+
apiKey;
|
|
80
|
+
fetchFn;
|
|
81
|
+
timeoutMs;
|
|
82
|
+
maxResponseBytes;
|
|
83
|
+
maxToolArgBytes;
|
|
84
|
+
contextWindow;
|
|
85
|
+
temperature;
|
|
86
|
+
maxTokens;
|
|
87
|
+
constructor(config) {
|
|
88
|
+
this.model = config.model;
|
|
89
|
+
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
90
|
+
this.apiKey = config.apiKey ?? "";
|
|
91
|
+
this.fetchFn = config.fetchFn ?? fetch;
|
|
92
|
+
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
93
|
+
this.maxResponseBytes = config.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
|
|
94
|
+
this.maxToolArgBytes = config.maxToolArgBytes ?? DEFAULT_MAX_TOOL_ARG_BYTES;
|
|
95
|
+
this.contextWindow = config.contextWindowSize ?? DEFAULT_CONTEXT_WINDOW;
|
|
96
|
+
this.temperature = config.temperature;
|
|
97
|
+
this.maxTokens = config.maxTokens;
|
|
98
|
+
}
|
|
99
|
+
async complete(messages, tools) {
|
|
100
|
+
const payload = {
|
|
101
|
+
model: this.model,
|
|
102
|
+
messages: messages.map(toOpenAIMessage),
|
|
103
|
+
};
|
|
104
|
+
if (this.temperature !== undefined) {
|
|
105
|
+
payload.temperature = this.temperature;
|
|
106
|
+
}
|
|
107
|
+
if (this.maxTokens !== undefined) {
|
|
108
|
+
payload.max_tokens = this.maxTokens;
|
|
109
|
+
}
|
|
110
|
+
if (tools && tools.length > 0) {
|
|
111
|
+
payload.tools = tools.map(toOpenAITool);
|
|
112
|
+
payload.tool_choice = "auto";
|
|
113
|
+
}
|
|
114
|
+
const headers = {
|
|
115
|
+
"Content-Type": "application/json",
|
|
116
|
+
};
|
|
117
|
+
if (this.apiKey) {
|
|
118
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
119
|
+
}
|
|
120
|
+
const controller = new AbortController();
|
|
121
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
122
|
+
try {
|
|
123
|
+
let res;
|
|
124
|
+
try {
|
|
125
|
+
res = await this.fetchFn(`${this.baseUrl}/chat/completions`, {
|
|
126
|
+
method: "POST",
|
|
127
|
+
headers,
|
|
128
|
+
body: JSON.stringify(payload),
|
|
129
|
+
signal: controller.signal,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
if (controller.signal.aborted ||
|
|
134
|
+
(err instanceof Error && err.name === "AbortError")) {
|
|
135
|
+
throw new OpenAIError(`request timed out after ${this.timeoutMs}ms`);
|
|
136
|
+
}
|
|
137
|
+
throw new OpenAIError(`request failed: ${toError(err).message}`);
|
|
138
|
+
}
|
|
139
|
+
const text = await res.text();
|
|
140
|
+
if (text.length > this.maxResponseBytes) {
|
|
141
|
+
throw new OpenAIError(`response body exceeds limit of ${this.maxResponseBytes} bytes`);
|
|
142
|
+
}
|
|
143
|
+
if (!res.ok) {
|
|
144
|
+
throw new OpenAIError(`HTTP ${res.status}: ${truncateDetail(text)}`, res.status);
|
|
145
|
+
}
|
|
146
|
+
let data;
|
|
147
|
+
try {
|
|
148
|
+
data = JSON.parse(text);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
throw new OpenAIError("malformed JSON in response body");
|
|
152
|
+
}
|
|
153
|
+
return this.parseCompletion(data);
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
clearTimeout(timer);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
parseCompletion(data) {
|
|
160
|
+
const completion = data;
|
|
161
|
+
const choice = completion.choices?.[0];
|
|
162
|
+
const message = choice?.message;
|
|
163
|
+
if (!message) {
|
|
164
|
+
throw new OpenAIError("unexpected response shape: missing choices[0].message");
|
|
165
|
+
}
|
|
166
|
+
const toolCalls = [];
|
|
167
|
+
if (message.tool_calls) {
|
|
168
|
+
for (let i = 0; i < message.tool_calls.length; i++) {
|
|
169
|
+
const tc = message.tool_calls[i];
|
|
170
|
+
const name = tc.function?.name;
|
|
171
|
+
if (!name) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
toolCalls.push({
|
|
175
|
+
id: tc.id ?? `call_${i}`,
|
|
176
|
+
name,
|
|
177
|
+
arguments: parseToolArguments(tc.function?.arguments, this.maxToolArgBytes),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const usage = {
|
|
182
|
+
prompt_tokens: completion.usage?.prompt_tokens ?? 0,
|
|
183
|
+
completion_tokens: completion.usage?.completion_tokens ?? 0,
|
|
184
|
+
total_tokens: completion.usage?.total_tokens ?? 0,
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
content: message.content ?? "",
|
|
188
|
+
reasoning: message.reasoning_content ?? "",
|
|
189
|
+
tool_calls: toolCalls,
|
|
190
|
+
finish_reason: choice?.finish_reason ?? "",
|
|
191
|
+
usage_tokens: usage,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
supportsNativeToolCalling() {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
modelName() {
|
|
198
|
+
return this.model;
|
|
199
|
+
}
|
|
200
|
+
contextWindowSize() {
|
|
201
|
+
return this.contextWindow;
|
|
202
|
+
}
|
|
203
|
+
}
|
package/dist/llm/response.d.ts
CHANGED
|
@@ -10,6 +10,11 @@ export interface TokenUsage {
|
|
|
10
10
|
}
|
|
11
11
|
export interface Response {
|
|
12
12
|
content: string;
|
|
13
|
+
/** Raw reasoning returned on a separate channel (e.g. reasoning_content for
|
|
14
|
+
* DeepSeek-style models). Never written into content, never returned to the
|
|
15
|
+
* end user, and never recorded on a default audit record; consumers
|
|
16
|
+
* normalize it with the reasoning adapter. */
|
|
17
|
+
reasoning: string;
|
|
13
18
|
tool_calls: ToolCall[];
|
|
14
19
|
finish_reason: string;
|
|
15
20
|
usage_tokens: TokenUsage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/llm/response.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,UAAU,CAAC;CAC1B"}
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/llm/response.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB;;;kDAG8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,UAAU,CAAC;CAC1B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response_validator.d.ts","sourceRoot":"","sources":["../../../src/middleware/guardrails/response_validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAgC,MAAM,
|
|
1
|
+
{"version":3,"file":"response_validator.d.ts","sourceRoot":"","sources":["../../../src/middleware/guardrails/response_validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAgC,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,YAAY,CAAsD;IAE1E,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,mBAAmB,CAAS;gBAGlC,SAAS,EAAE,MAAM,EAAE,EACnB,aAAa,GAAE,OAAc,EAC7B,YAAY,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,KAAK;IAapE,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB;IAYxD,iBAAiB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,gBAAgB;IAoB1D,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,oBAAoB;IAqC5B,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,yBAAyB;IA0BjC,OAAO,CAAC,uBAAuB;CAGhC"}
|
|
@@ -2,4 +2,9 @@ export { MiddlewareImpl, Middleware, ToolExecutor } from "./middleware.js";
|
|
|
2
2
|
export { Action, CallerContext, Decision, MessageType, MessageMeta } from "./types.js";
|
|
3
3
|
export { PolicyEvaluator, Policy, Rule, Condition, Operator, SimplePolicyEvaluator } from "./policy.js";
|
|
4
4
|
export { Auditor, AuditRecord, InMemoryAuditor, AuditFilter } from "./audit.js";
|
|
5
|
+
export { runInference, InferenceConfig, InferenceResult, RetriesExhaustedError, } from "./inference.js";
|
|
6
|
+
export { ResponseValidator, ValidationResult, ToolCall as ValidatedToolCall, } from "./guardrails/response_validator.js";
|
|
7
|
+
export { Nudge, NudgeKind, retryNudge, unknownToolNudge, stepNudge, prerequisiteNudge, } from "./guardrails/nudge.js";
|
|
8
|
+
export { ErrorTracker, ErrorCategory, ToolError, } from "./guardrails/error_tracker.js";
|
|
9
|
+
export { StepEnforcer, StepNotAllowedError } from "./guardrails/step_enforcer.js";
|
|
5
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACxG,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACxG,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EACL,YAAY,EACZ,eAAe,EACf,eAAe,EACf,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,IAAI,iBAAiB,GAC9B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,KAAK,EACL,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,YAAY,EACZ,aAAa,EACb,SAAS,GACV,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC"}
|
package/dist/middleware/index.js
CHANGED
|
@@ -2,3 +2,8 @@ export { MiddlewareImpl } from "./middleware.js";
|
|
|
2
2
|
export { Action, MessageType } from "./types.js";
|
|
3
3
|
export { Operator, SimplePolicyEvaluator } from "./policy.js";
|
|
4
4
|
export { InMemoryAuditor } from "./audit.js";
|
|
5
|
+
export { runInference, RetriesExhaustedError, } from "./inference.js";
|
|
6
|
+
export { ResponseValidator, } from "./guardrails/response_validator.js";
|
|
7
|
+
export { NudgeKind, retryNudge, unknownToolNudge, stepNudge, prerequisiteNudge, } from "./guardrails/nudge.js";
|
|
8
|
+
export { ErrorTracker, ErrorCategory, } from "./guardrails/error_tracker.js";
|
|
9
|
+
export { StepEnforcer, StepNotAllowedError } from "./guardrails/step_enforcer.js";
|
|
@@ -5,6 +5,7 @@ import type { ContextWindowManager } from "../llmcontext/context_window.js";
|
|
|
5
5
|
import type { ResponseValidator } from "./guardrails/response_validator.js";
|
|
6
6
|
import { ErrorTracker } from "./guardrails/error_tracker.js";
|
|
7
7
|
import { StepEnforcer } from "./guardrails/step_enforcer.js";
|
|
8
|
+
import { ReasoningAdapter, type ContextTree } from "../reasoning/index.js";
|
|
8
9
|
export declare class RetriesExhaustedError extends Error {
|
|
9
10
|
constructor(message: string);
|
|
10
11
|
}
|
|
@@ -13,6 +14,10 @@ export interface InferenceResult {
|
|
|
13
14
|
newMessages: Message[];
|
|
14
15
|
toolCallCounter: number;
|
|
15
16
|
attempts: number;
|
|
17
|
+
/** Normalized context tree for the final turn's reasoning (native
|
|
18
|
+
* reasoning_content and/or thinking tags). Null when the turn carried no
|
|
19
|
+
* reasoning. Holds bounded summaries only; raw reasoning is never attached. */
|
|
20
|
+
reasoningTree: ContextTree | null;
|
|
16
21
|
}
|
|
17
22
|
export interface InferenceConfig {
|
|
18
23
|
client: Backend;
|
|
@@ -23,6 +28,9 @@ export interface InferenceConfig {
|
|
|
23
28
|
toolSpecs: ToolDefinition[];
|
|
24
29
|
maxAttempts: number;
|
|
25
30
|
stepIndex?: number;
|
|
31
|
+
/** Optional reasoning adapter override for limits and registered model
|
|
32
|
+
* fields. */
|
|
33
|
+
reasoning?: ReasoningAdapter;
|
|
26
34
|
}
|
|
27
35
|
export declare function runInference(messages: Message[], cfg: InferenceConfig, sessionId?: string): Promise<InferenceResult | null>;
|
|
28
36
|
//# sourceMappingURL=inference.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inference.d.ts","sourceRoot":"","sources":["../../src/middleware/inference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAA2B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAG5E,OAAO,KAAK,EAAE,iBAAiB,EAA8B,MAAM,oCAAoC,CAAC;AACxG,OAAO,EAAE,YAAY,EAAiB,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"inference.d.ts","sourceRoot":"","sources":["../../src/middleware/inference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAA2B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAG5E,OAAO,KAAK,EAAE,iBAAiB,EAA8B,MAAM,oCAAoC,CAAC;AACxG,OAAO,EAAE,YAAY,EAAiB,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAkB,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAE3F,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,OAAO,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB;;mFAE+E;IAC/E,aAAa,EAAE,WAAW,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;iBACa;IACb,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAKD,wBAAsB,YAAY,CAChC,QAAQ,EAAE,OAAO,EAAE,EACnB,GAAG,EAAE,eAAe,EACpB,SAAS,GAAE,MAAW,GACrB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CA8LjC"}
|
|
@@ -2,12 +2,15 @@ import { Role } from "../llm/request.js";
|
|
|
2
2
|
import { MessageType } from "./types.js";
|
|
3
3
|
import { ErrorCategory } from "./guardrails/error_tracker.js";
|
|
4
4
|
import { stepNudge } from "./guardrails/nudge.js";
|
|
5
|
+
import { ReasoningAdapter, ReasoningError } from "../reasoning/index.js";
|
|
5
6
|
export class RetriesExhaustedError extends Error {
|
|
6
7
|
constructor(message) {
|
|
7
8
|
super(message);
|
|
8
9
|
this.name = "RetriesExhaustedError";
|
|
9
10
|
}
|
|
10
11
|
}
|
|
12
|
+
// Default adapter strips and normalizes reasoning in the inference loop.
|
|
13
|
+
const defaultAdapter = new ReasoningAdapter();
|
|
11
14
|
export async function runInference(messages, cfg, sessionId = "") {
|
|
12
15
|
let maxAttempts = cfg.maxAttempts;
|
|
13
16
|
if (maxAttempts <= 0) {
|
|
@@ -49,43 +52,81 @@ export async function runInference(messages, cfg, sessionId = "") {
|
|
|
49
52
|
cfg.contextManager.updateTokenCount(resp.usage_tokens.total_tokens);
|
|
50
53
|
}
|
|
51
54
|
let validationResult = null;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
// AR-1: normalize reasoning and strip it before ordinary tool-call
|
|
56
|
+
// parsing. Reasoning can arrive as a native reasoning_content field or as
|
|
57
|
+
// embedded thinking tags in content; both are combined into a synthetic
|
|
58
|
+
// structured input so the adapter sees the full picture. Malformed or
|
|
59
|
+
// unbounded reasoning fails closed: this turn is treated as invalid and
|
|
60
|
+
// retried rather than parsed partially.
|
|
61
|
+
let reasoningTree = null;
|
|
62
|
+
let cleanContent = resp.content;
|
|
63
|
+
let reasoningFailed = false;
|
|
64
|
+
if (resp.content || resp.reasoning) {
|
|
65
|
+
const reasoningInput = { content: resp.content };
|
|
66
|
+
if (resp.reasoning) {
|
|
67
|
+
reasoningInput["reasoning_content"] = resp.reasoning;
|
|
59
68
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
needsRetry: false,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
else if (resp.content) {
|
|
69
|
-
if (cfg.validator) {
|
|
70
|
-
validationResult = cfg.validator.validateTextResponse(resp.content);
|
|
69
|
+
const adapter = cfg.reasoning ?? defaultAdapter;
|
|
70
|
+
try {
|
|
71
|
+
reasoningTree = adapter.extract(reasoningInput, cfg.client.modelName(), "llm-backend");
|
|
72
|
+
cleanContent = adapter.strip(reasoningInput);
|
|
71
73
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
catch (e) {
|
|
75
|
+
if (e instanceof ReasoningError) {
|
|
76
|
+
reasoningFailed = true;
|
|
77
|
+
// Fail closed: never echo content that may carry reasoning we could
|
|
78
|
+
// not parse.
|
|
79
|
+
cleanContent = "";
|
|
80
|
+
validationResult = {
|
|
81
|
+
toolCalls: [],
|
|
82
|
+
nudge: null,
|
|
83
|
+
needsRetry: true,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
throw e;
|
|
88
|
+
}
|
|
74
89
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
90
|
+
}
|
|
91
|
+
if (!reasoningFailed) {
|
|
92
|
+
if (resp.tool_calls && resp.tool_calls.length > 0) {
|
|
93
|
+
const guardrailsToolCalls = resp.tool_calls.map((tc) => ({
|
|
94
|
+
tool: tc.name,
|
|
95
|
+
args: tc.arguments,
|
|
80
96
|
}));
|
|
97
|
+
if (cfg.validator) {
|
|
98
|
+
validationResult = cfg.validator.validateToolCalls(guardrailsToolCalls);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
validationResult = {
|
|
102
|
+
toolCalls: guardrailsToolCalls,
|
|
103
|
+
nudge: null,
|
|
104
|
+
needsRetry: false,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
81
107
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
108
|
+
else if (cleanContent) {
|
|
109
|
+
if (cfg.validator) {
|
|
110
|
+
validationResult = cfg.validator.validateTextResponse(cleanContent);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
validationResult = { toolCalls: [], nudge: null, needsRetry: false };
|
|
114
|
+
}
|
|
115
|
+
if (!validationResult.needsRetry && validationResult.toolCalls.length > 0) {
|
|
116
|
+
resp.tool_calls = validationResult.toolCalls.map((tc) => ({
|
|
117
|
+
id: "",
|
|
118
|
+
name: tc.tool,
|
|
119
|
+
arguments: tc.args,
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
86
122
|
}
|
|
87
123
|
else {
|
|
88
|
-
|
|
124
|
+
if (cfg.validator) {
|
|
125
|
+
validationResult = cfg.validator.validateTextResponse("");
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
validationResult = { toolCalls: [], nudge: null, needsRetry: true };
|
|
129
|
+
}
|
|
89
130
|
}
|
|
90
131
|
}
|
|
91
132
|
lastResponse = resp;
|
|
@@ -114,6 +155,7 @@ export async function runInference(messages, cfg, sessionId = "") {
|
|
|
114
155
|
newMessages: currentMessages,
|
|
115
156
|
toolCallCounter,
|
|
116
157
|
attempts,
|
|
158
|
+
reasoningTree,
|
|
117
159
|
};
|
|
118
160
|
}
|
|
119
161
|
if (cfg.errorTracker) {
|
|
@@ -132,7 +174,7 @@ export async function runInference(messages, cfg, sessionId = "") {
|
|
|
132
174
|
}
|
|
133
175
|
const failedMsg = {
|
|
134
176
|
role: Role.ASSISTANT,
|
|
135
|
-
content:
|
|
177
|
+
content: cleanContent,
|
|
136
178
|
meta: { type: MessageType.TEXT_RESPONSE },
|
|
137
179
|
};
|
|
138
180
|
currentMessages.push(failedMsg);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ReasoningError } from "./errors.js";
|
|
2
|
+
import { ReasoningOptions } from "./options.js";
|
|
3
|
+
import { ContextTree } from "./types.js";
|
|
4
|
+
export { ReasoningError };
|
|
5
|
+
export declare class ReasoningAdapter {
|
|
6
|
+
private readonly opts;
|
|
7
|
+
private readonly modelFields;
|
|
8
|
+
constructor(options?: ReasoningOptions);
|
|
9
|
+
get options(): Required<ReasoningOptions>;
|
|
10
|
+
/** Map a model name to a model-specific reasoning field name. Fields
|
|
11
|
+
* registered here are consulted before the known field set. */
|
|
12
|
+
registerModelField(model: string, fieldName: string): void;
|
|
13
|
+
/** Remove reasoning from raw and return the clean content that ordinary
|
|
14
|
+
* tool-call parsers should see. Fails closed. */
|
|
15
|
+
strip(raw: unknown): string;
|
|
16
|
+
/** Normalize reasoning found in raw into a versioned context tree. Raw
|
|
17
|
+
* reasoning is never stored on the tree: nodes carry bounded summaries. */
|
|
18
|
+
extract(raw: unknown, model?: string, backend?: string): ContextTree;
|
|
19
|
+
/** Strip and extract in one call: return the clean content and tree. */
|
|
20
|
+
parse(raw: unknown, model?: string, backend?: string): {
|
|
21
|
+
clean: string;
|
|
22
|
+
tree: ContextTree;
|
|
23
|
+
};
|
|
24
|
+
private analyze;
|
|
25
|
+
private analyzeString;
|
|
26
|
+
private analyzeBytes;
|
|
27
|
+
private analyzeStructured;
|
|
28
|
+
private extractReasoningField;
|
|
29
|
+
private analyzeText;
|
|
30
|
+
private hasRecognizedKey;
|
|
31
|
+
private parseNodes;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/reasoning/adapter.ts"],"names":[],"mappings":"AAcA,OAAO,EAKL,cAAc,EAKf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAoB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAoB,WAAW,EAAsC,MAAM,YAAY,CAAC;AAE/F,OAAO,EAAE,cAAc,EAAE,CAAC;AAmE1B,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;gBAEtC,OAAO,CAAC,EAAE,gBAAgB;IAKtC,IAAI,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAExC;IAED;mEAC+D;IAC/D,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAM1D;qDACiD;IACjD,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM;IAI3B;+EAC2E;IAC3E,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,SAAK,EAAE,OAAO,SAAK,GAAG,WAAW;IA0B5D,wEAAwE;IACxE,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,SAAK,EAAE,OAAO,SAAK,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE;IAKnF,OAAO,CAAC,OAAO;IAaf,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,qBAAqB;IAqC7B,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,UAAU;CA2DnB"}
|