freddie 0.0.116 → 0.0.117
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
CHANGED
|
@@ -62,9 +62,15 @@ export default {
|
|
|
62
62
|
let model = opts.model || undefined
|
|
63
63
|
if (!provider && model && /^[a-z][a-z0-9-]*\//.test(model)) { provider = model.split('/')[0]; model = model.slice(provider.length + 1) }
|
|
64
64
|
const out = await runTurn({ prompt: opts.prompt, provider, model, skill: opts.skill || undefined, cwd: opts.cwd || undefined, timeoutMs: Number(opts.timeout), witnessPath: opts.witness || undefined })
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
process.exit(
|
|
65
|
+
console.log(out.error ? '' : (out.result || out.messages?.at(-1)?.content || ''))
|
|
66
|
+
if (out.error) console.error('error:', out.error)
|
|
67
|
+
// Tear down cleanly instead of process.exit(): force-closing the
|
|
68
|
+
// process while undici keep-alive sockets and a pending setImmediate
|
|
69
|
+
// are still live makes libuv double-close its async handle and assert
|
|
70
|
+
// UV_HANDLE_CLOSING on Windows. Close the HTTP dispatcher's sockets,
|
|
71
|
+
// then set exitCode and let the event loop drain on its own.
|
|
72
|
+
try { const u = await import('undici'); await u.getGlobalDispatcher()?.close?.() } catch {}
|
|
73
|
+
process.exitCode = out.error ? 1 : 0
|
|
68
74
|
} })
|
|
69
75
|
C({ name: 'cron', description: 'Manage cron jobs', args: [{ name: 'action', default: 'list' }, { name: 'a1' }, { name: 'a2' }], action: async (action, a1, a2) => {
|
|
70
76
|
const { listJobs, createJob, cancelJob, deleteJob, tick } = await import('../../src/cron/scheduler.js')
|
|
@@ -2,6 +2,32 @@ import { logger } from '../observability/log.js'
|
|
|
2
2
|
|
|
3
3
|
const log = logger('acptoapi')
|
|
4
4
|
|
|
5
|
+
// acptoapi may take minutes to answer when it serially walks dead providers
|
|
6
|
+
// (each ~90s timeout) before a live one responds. Node's default undici
|
|
7
|
+
// headersTimeout/bodyTimeout (~300s) would throw UND_ERR_HEADERS_TIMEOUT
|
|
8
|
+
// mid-walk, so we raise them to 0 (disabled) on the global dispatcher and let
|
|
9
|
+
// our AbortController be the single source of truth for the overall deadline
|
|
10
|
+
// (default 240s, override via FREDDIE_LLM_TIMEOUT_MS). Use setGlobalDispatcher
|
|
11
|
+
// once rather than a per-request Agent so we don't leak a dispatcher per call.
|
|
12
|
+
const ACPTOAPI_TIMEOUT_MS = Number(process.env.FREDDIE_LLM_TIMEOUT_MS) || 240000
|
|
13
|
+
let _dispatcherSet = false
|
|
14
|
+
async function ensureLongTimeoutDispatcher() {
|
|
15
|
+
if (_dispatcherSet) return
|
|
16
|
+
_dispatcherSet = true
|
|
17
|
+
try {
|
|
18
|
+
const undici = await import('undici')
|
|
19
|
+
// headersTimeout/bodyTimeout 0: tolerate acptoapi's minutes-long walk.
|
|
20
|
+
// keepAlive*Timeout 1ms: close the socket right after the response so no
|
|
21
|
+
// keep-alive socket lingers between calls.
|
|
22
|
+
undici.setGlobalDispatcher(new undici.Agent({
|
|
23
|
+
headersTimeout: 0,
|
|
24
|
+
bodyTimeout: 0,
|
|
25
|
+
keepAliveTimeout: 1,
|
|
26
|
+
keepAliveMaxTimeout: 1,
|
|
27
|
+
}))
|
|
28
|
+
} catch { /* undici not available — rely on AbortController + defaults */ }
|
|
29
|
+
}
|
|
30
|
+
|
|
5
31
|
export function getAcptoapiUrl() {
|
|
6
32
|
return process.env.FREDDIE_LLM_URL || 'http://127.0.0.1:4800/v1'
|
|
7
33
|
}
|
|
@@ -36,8 +62,9 @@ export async function callLLM({ messages, tools = [], model } = {}) {
|
|
|
36
62
|
// Network Access headers so cross-origin loopback (gh-pages → localhost)
|
|
37
63
|
// succeeds when acptoapi is running. The earlier preemptive loopback
|
|
38
64
|
// refusal caused false negatives on reachable endpoints.
|
|
65
|
+
await ensureLongTimeoutDispatcher()
|
|
39
66
|
const _ac = new AbortController()
|
|
40
|
-
const _tid = setTimeout(() => _ac.abort(new Error('acptoapi fetch timeout')),
|
|
67
|
+
const _tid = setTimeout(() => _ac.abort(new Error('acptoapi fetch timeout')), ACPTOAPI_TIMEOUT_MS)
|
|
41
68
|
let res
|
|
42
69
|
try {
|
|
43
70
|
res = await fetch(base.replace(/\/$/, '') + '/chat/completions', {
|
package/src/agent/machine.js
CHANGED
|
@@ -172,16 +172,22 @@ export async function runTurn({ prompt, messages = [], model, provider, callLLM,
|
|
|
172
172
|
const machine = createAgentMachine({ model, provider, callLLM, enabledToolsets, disabledToolsets, maxIterations, events })
|
|
173
173
|
const actor = createActor(machine, { input: { messages: initMessages } }); actor.start(); actor.send({ type: 'SUBMIT', prompt })
|
|
174
174
|
return await new Promise((resolve, reject) => {
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
let sub
|
|
176
|
+
const cleanup = () => { try { sub?.unsubscribe() } catch {} try { actor.stop() } catch {} }
|
|
177
|
+
const t = setTimeout(() => { cleanup(); reject(new Error('agent turn timeout')) }, timeoutMs)
|
|
178
|
+
sub = actor.subscribe(snap => { if (snap.status !== 'done') return; clearTimeout(t)
|
|
177
179
|
;(async () => {
|
|
178
180
|
const out = snap.output
|
|
179
181
|
const outbound = await h.hooks.invoke('onMessageOutbound', { content: out?.result || '' })
|
|
180
182
|
if (outbound?.systemMessage || outbound?.additionalContext) out.messages = mergeHookExtras(out.messages || [], outbound, 'onMessageOutbound')
|
|
181
183
|
await h.hooks.invoke('onSessionEnd', { reason: out?.error ? 'error' : 'ok', iterations: out?.iterations })
|
|
182
184
|
const errorStack = out?.error ? (events.find(e => e.type === 'llm_call' && !e.ok)?.stack || null) : null
|
|
183
|
-
await writeTrajectory(out, { prompt, provider, model, skill, cwd, events, errorStack, witnessPath })
|
|
184
|
-
|
|
185
|
+
await writeTrajectory(out, { prompt, provider, model, skill, cwd, events, errorStack, witnessPath })
|
|
186
|
+
// Unsubscribe + stop the actor once the turn is done — a finished
|
|
187
|
+
// actor should not be left running with live subscriptions/handles.
|
|
188
|
+
cleanup()
|
|
189
|
+
resolve(out)
|
|
190
|
+
})().catch(e => { cleanup(); reject(e) })
|
|
185
191
|
})
|
|
186
192
|
})
|
|
187
193
|
}
|