@hallofame/cloudflare-agent 0.1.15 → 0.1.16
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 +1 -1
- package/src/HallOfFameAgent.ts +35 -11
- package/src/WorkersAIResponse.ts +4 -2
package/package.json
CHANGED
package/src/HallOfFameAgent.ts
CHANGED
|
@@ -12,6 +12,7 @@ const TOKEN_KEY = 'halloffame-token'
|
|
|
12
12
|
const DEFAULT_MODEL = '@cf/google/gemma-4-26b-a4b-it'
|
|
13
13
|
const DEFAULT_INTERVAL_SECONDS = 18_000
|
|
14
14
|
const MAX_TOOL_STEPS = 12
|
|
15
|
+
const MAX_EMPTY_MODEL_RETRIES = 2
|
|
15
16
|
const MAX_REQUEST_FAILURES = 20
|
|
16
17
|
|
|
17
18
|
type ModelMessage = Record<string, unknown>
|
|
@@ -193,18 +194,8 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
|
|
|
193
194
|
|
|
194
195
|
try {
|
|
195
196
|
for (let step = 0; step < MAX_TOOL_STEPS; step += 1) {
|
|
196
|
-
const
|
|
197
|
-
messages,
|
|
198
|
-
tools,
|
|
199
|
-
max_tokens: 1024,
|
|
200
|
-
...(visionImage ? { image: visionImage } : {}),
|
|
201
|
-
}
|
|
197
|
+
const result = await this.requestModel(messages, visionImage)
|
|
202
198
|
visionImage = undefined
|
|
203
|
-
const rawResult = await this.env.AI.run(
|
|
204
|
-
(this.env.HOF_MODEL ?? DEFAULT_MODEL) as keyof AiModels,
|
|
205
|
-
input as never,
|
|
206
|
-
)
|
|
207
|
-
const result = new WorkersAIResponse(rawResult).normalize()
|
|
208
199
|
const calls = this.parseToolCalls(result.toolCalls)
|
|
209
200
|
|
|
210
201
|
if (calls.length === 0) {
|
|
@@ -258,6 +249,39 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
|
|
|
258
249
|
}
|
|
259
250
|
}
|
|
260
251
|
|
|
252
|
+
private async requestModel(
|
|
253
|
+
messages: ModelMessage[],
|
|
254
|
+
visionImage?: string,
|
|
255
|
+
): Promise<ReturnType<WorkersAIResponse['normalize']>> {
|
|
256
|
+
for (let attempt = 0; attempt <= MAX_EMPTY_MODEL_RETRIES; attempt += 1) {
|
|
257
|
+
const retryInstruction =
|
|
258
|
+
attempt > 0
|
|
259
|
+
? [
|
|
260
|
+
{
|
|
261
|
+
role: 'system',
|
|
262
|
+
content:
|
|
263
|
+
'The previous model turn was empty. Continue from the existing conversation. Return the required tool call, or a concise factual final response if the task is complete.',
|
|
264
|
+
},
|
|
265
|
+
]
|
|
266
|
+
: []
|
|
267
|
+
const rawResult = await this.env.AI.run(
|
|
268
|
+
(this.env.HOF_MODEL ?? DEFAULT_MODEL) as keyof AiModels,
|
|
269
|
+
{
|
|
270
|
+
messages: [...messages, ...retryInstruction],
|
|
271
|
+
tools,
|
|
272
|
+
max_tokens: 1024,
|
|
273
|
+
...(visionImage ? { image: visionImage } : {}),
|
|
274
|
+
} as never,
|
|
275
|
+
)
|
|
276
|
+
const result = new WorkersAIResponse(rawResult).normalize()
|
|
277
|
+
if (result.text || result.toolCalls.length > 0) return result
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
throw new Error(
|
|
281
|
+
`Workers AI returned neither text nor tool calls after ${MAX_EMPTY_MODEL_RETRIES + 1} attempts.`,
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
|
|
261
285
|
private async executeTool(
|
|
262
286
|
call: ToolCall,
|
|
263
287
|
token: string,
|
package/src/WorkersAIResponse.ts
CHANGED
|
@@ -7,10 +7,12 @@ export class WorkersAIResponse {
|
|
|
7
7
|
constructor(private readonly value: unknown) {}
|
|
8
8
|
|
|
9
9
|
normalize(): NormalizedModelResponse {
|
|
10
|
-
const
|
|
10
|
+
const envelope = this.record(this.value)
|
|
11
|
+
const nested = this.record(envelope.result)
|
|
12
|
+
const result = Object.keys(nested).length > 0 ? nested : envelope
|
|
11
13
|
const choice = Array.isArray(result.choices) ? this.record(result.choices[0]) : {}
|
|
12
14
|
const message = this.record(choice.message)
|
|
13
|
-
const content = result.response ?? message.content
|
|
15
|
+
const content = result.response ?? result.output_text ?? message.content
|
|
14
16
|
|
|
15
17
|
return {
|
|
16
18
|
text: this.text(content),
|