@belte/anthropic 0.1.0 → 0.2.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/package.json +2 -2
- package/src/engine.ts +30 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@belte/anthropic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Anthropic Messages API engine for belte's agent() — a model-of-your-choosing assistant over your app's MCP surface",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,6 +39,6 @@
|
|
|
39
39
|
"@anthropic-ai/sdk": "^0.102.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@belte/belte": ">=0.
|
|
42
|
+
"@belte/belte": ">=0.19.2"
|
|
43
43
|
}
|
|
44
44
|
}
|
package/src/engine.ts
CHANGED
|
@@ -24,10 +24,18 @@ are no provider built-ins to fence here.
|
|
|
24
24
|
type AnthropicConfig = {
|
|
25
25
|
model: string
|
|
26
26
|
apiKey: string
|
|
27
|
+
// Alternate Messages API origin — a gateway, a proxy, or a test double.
|
|
28
|
+
baseURL?: string
|
|
27
29
|
maxTokens?: number
|
|
28
30
|
effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max'
|
|
31
|
+
// Hard cap on tool-loop turns so a model that never stops requesting tools can't
|
|
32
|
+
// spin the loop (and the open stream) forever. Defaults to MAX_STEPS.
|
|
33
|
+
maxSteps?: number
|
|
29
34
|
}
|
|
30
35
|
|
|
36
|
+
// Default tool-loop bound — generous enough for real multi-step tasks, finite enough to stop a runaway.
|
|
37
|
+
const MAX_STEPS = 24
|
|
38
|
+
|
|
31
39
|
// Provider stop_reason → the loop's neutral stop signal.
|
|
32
40
|
function mapStop(
|
|
33
41
|
stopReason: Anthropic.Message['stop_reason'],
|
|
@@ -97,12 +105,21 @@ function toolResultText(result: Record<string, unknown>): string {
|
|
|
97
105
|
}
|
|
98
106
|
|
|
99
107
|
export function engine(config: AnthropicConfig): AgentEngine {
|
|
100
|
-
const client = new Anthropic({ apiKey: config.apiKey })
|
|
108
|
+
const client = new Anthropic({ apiKey: config.apiKey, baseURL: config.baseURL })
|
|
109
|
+
const maxSteps = config.maxSteps ?? MAX_STEPS
|
|
101
110
|
return async function* ({ surface, messages }) {
|
|
102
|
-
|
|
111
|
+
/*
|
|
112
|
+
Drop turns that serialize to empty content — an assistant turn with no text
|
|
113
|
+
and no tool uses, or a tool turn with no results. The Messages API rejects an
|
|
114
|
+
empty content-block array, so a caller replaying such a turn would 400 the
|
|
115
|
+
whole request.
|
|
116
|
+
*/
|
|
117
|
+
const conversation: Anthropic.MessageParam[] = messages
|
|
118
|
+
.map(toAnthropicMessage)
|
|
119
|
+
.filter((message) => !(Array.isArray(message.content) && message.content.length === 0))
|
|
103
120
|
const tools = surface.tools.map(toAnthropicTool)
|
|
104
121
|
|
|
105
|
-
|
|
122
|
+
for (let step = 0; ; step += 1) {
|
|
106
123
|
const stream = client.messages.stream({
|
|
107
124
|
model: config.model,
|
|
108
125
|
max_tokens: config.maxTokens ?? 64000,
|
|
@@ -122,10 +139,20 @@ export function engine(config: AnthropicConfig): AgentEngine {
|
|
|
122
139
|
const final = await stream.finalMessage()
|
|
123
140
|
conversation.push({ role: 'assistant', content: final.content })
|
|
124
141
|
|
|
142
|
+
// Server paused a long-running turn: resume by re-requesting with the turn
|
|
143
|
+
// so far rather than ending and truncating the output. The step cap bounds it.
|
|
144
|
+
if (final.stop_reason === 'pause_turn' && step + 1 < maxSteps) {
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
125
147
|
if (final.stop_reason !== 'tool_use') {
|
|
126
148
|
yield { type: 'done', stop: mapStop(final.stop_reason) }
|
|
127
149
|
return
|
|
128
150
|
}
|
|
151
|
+
if (step + 1 >= maxSteps) {
|
|
152
|
+
// Tool-loop cap hit: stop instead of dispatching another round.
|
|
153
|
+
yield { type: 'done', stop: 'error' }
|
|
154
|
+
return
|
|
155
|
+
}
|
|
129
156
|
|
|
130
157
|
const results: Anthropic.ToolResultBlockParam[] = []
|
|
131
158
|
for (const block of final.content) {
|