freddie 0.0.98 → 0.0.100

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freddie",
3
- "version": "0.0.98",
3
+ "version": "0.0.100",
4
4
  "type": "module",
5
5
  "description": "Open JS agent harness built on pi-mono, floosie, xstate, and anentrypoint-design",
6
6
  "bin": {
@@ -27,7 +27,7 @@
27
27
  "xstate": "^5.31.0",
28
28
  "zod": "^4.0.0",
29
29
  "anentrypoint-design": "^0.0.94",
30
- "acptoapi": "^1.0.59"
30
+ "acptoapi": "^1.0.63"
31
31
  },
32
32
  "optionalDependencies": {
33
33
  "@libsql/darwin-arm64": "0.3.19",
@@ -1,44 +1,44 @@
1
- import { resolveCallLLM } from '../../src/agent/llm_resolver.js'
1
+ import { createRequire } from 'module'
2
+ import { getConfigValue } from '../../src/config.js'
3
+ import { MATRIX_FILE } from '../../src/agent/model-matrix.js'
2
4
  import { flattenForOpenAI } from '../../src/agent/model-discovery.js'
3
5
  import { logger } from '../../src/observability/log.js'
4
6
 
7
+ const _require = createRequire(import.meta.url)
8
+ const sdk = _require('acptoapi')
5
9
  const log = logger('gui-llm-passthrough')
6
10
 
11
+ function matrixSource() { return process.env.FREDDIE_MATRIX_URL || MATRIX_FILE }
12
+
7
13
  export default {
8
14
  name: 'gui-llm-passthrough', surfaces: 'gui',
9
15
  register({ gui }) {
10
- gui.route('GET', '/v1/models', (_, res) => {
11
- const data = flattenForOpenAI()
12
- if (data.length === 0) data.push({ id: 'freddie/auto', object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'freddie' })
13
- res.json({ object: 'list', data })
16
+ gui.route('GET', '/v1/models', async (_, res) => {
17
+ try {
18
+ const rows = await sdk.listAllModelsAndQueues({ queuesMap: getConfigValue('agent.model_queues', {}) || {}, matrixSource: matrixSource() })
19
+ const local = flattenForOpenAI()
20
+ const seen = new Set(rows.map(r => r.id))
21
+ for (const r of local) if (!seen.has(r.id)) rows.push(r)
22
+ if (rows.length === 0) rows.push({ id: 'freddie/auto', object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'freddie' })
23
+ res.json({ object: 'list', data: rows })
24
+ } catch (e) { log.error('list-models-failed', { error: String(e.message || e) }); res.json({ object: 'list', data: flattenForOpenAI() }) }
14
25
  })
15
26
  gui.route('POST', '/v1/chat/completions', async (req, res) => {
16
27
  const { model, messages, tools, stream } = req.body || {}
17
28
  if (!Array.isArray(messages) || messages.length === 0) return res.status(400).json({ error: { message: 'messages required' } })
18
29
  try {
19
- let provider, mdl
20
- if (typeof model === 'string' && model.includes('/')) {
21
- const idx = model.indexOf('/'); provider = model.slice(0, idx); mdl = model.slice(idx + 1)
22
- }
23
- const call = resolveCallLLM({ provider, model: mdl })
24
- const out = await call({ messages, tools: tools?.map(t => ({ name: t.function?.name, description: t.function?.description, parameters: t.function?.parameters })) || [], model: mdl })
25
- const id = 'chatcmpl-' + Math.random().toString(36).slice(2, 12)
26
- const created = Math.floor(Date.now() / 1000)
27
- const choice = { index: 0, message: { role: 'assistant', content: out.content || '', ...(out.tool_calls?.length ? { tool_calls: out.tool_calls.map(tc => ({ id: tc.id, type: 'function', function: { name: tc.name, arguments: JSON.stringify(tc.arguments || {}) } })) } : {}) }, finish_reason: 'stop' }
30
+ const out = await sdk.chat({ model: model || 'freddie/auto', messages, tools, queuesMap: getConfigValue('agent.model_queues', {}) || {}, matrixSource: matrixSource(), output: 'openai' })
28
31
  if (stream) {
29
- res.setHeader('content-type', 'text/event-stream')
30
- res.setHeader('cache-control', 'no-cache')
31
- res.write(`data: ${JSON.stringify({ id, object: 'chat.completion.chunk', created, model: model || 'freddie/auto', choices: [{ index: 0, delta: { role: 'assistant', content: out.content || '' }, finish_reason: null }] })}\n\n`)
32
+ const id = 'chatcmpl-' + Math.random().toString(36).slice(2, 12)
33
+ const created = Math.floor(Date.now() / 1000)
34
+ const content = out?.choices?.[0]?.message?.content || ''
35
+ res.setHeader('content-type', 'text/event-stream'); res.setHeader('cache-control', 'no-cache')
36
+ res.write(`data: ${JSON.stringify({ id, object: 'chat.completion.chunk', created, model: model || 'freddie/auto', choices: [{ index: 0, delta: { role: 'assistant', content }, finish_reason: null }] })}\n\n`)
32
37
  res.write(`data: ${JSON.stringify({ id, object: 'chat.completion.chunk', created, model: model || 'freddie/auto', choices: [{ index: 0, delta: {}, finish_reason: 'stop' }] })}\n\n`)
33
- res.write('data: [DONE]\n\n')
34
- res.end()
35
- return
38
+ res.write('data: [DONE]\n\n'); res.end(); return
36
39
  }
37
- res.json({ id, object: 'chat.completion', created, model: model || 'freddie/auto', choices: [choice], usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 } })
38
- } catch (e) {
39
- log.error('chat-completions-failed', { error: String(e.message || e) })
40
- res.status(500).json({ error: { message: String(e.message || e), type: 'upstream_error' } })
41
- }
40
+ res.json(out)
41
+ } catch (e) { log.error('chat-completions-failed', { error: String(e.message || e) }); res.status(500).json({ error: { message: String(e.message || e), type: 'upstream_error' } }) }
42
42
  })
43
43
  },
44
44
  }
@@ -1,10 +1,12 @@
1
+ import { createRequire } from 'module'
1
2
  import { discoverAndPersist, listKnownProviders } from '../../src/agent/model-discovery.js'
2
- import { PROVIDER_KEYS, DEFAULTS } from '../../src/agent/llm_resolver.js'
3
3
  import { getConfigValue, saveConfigValue } from '../../src/config.js'
4
- import { getStatus } from '../../src/agent/model-sampler.js'
4
+ import { MATRIX_FILE } from '../../src/agent/model-matrix.js'
5
5
  import fs from 'node:fs'
6
6
  import path from 'node:path'
7
7
  import { spawn } from 'node:child_process'
8
+ const _require = createRequire(import.meta.url)
9
+ const { PROVIDER_KEYS, PROVIDER_DEFAULTS: DEFAULTS, getStatus, peekStatus, listAllQueues } = _require('acptoapi')
8
10
 
9
11
  const MATRIX_PATH = path.resolve(new URL('.', import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, '$1'), '..', '..', '.gm', 'model-availability.json')
10
12
  let _rebuildInFlight = null
@@ -18,7 +20,19 @@ export default {
18
20
  try { const provider = req.body?.provider; const result = await discoverAndPersist({ provider }); res.json(result) }
19
21
  catch (e) { res.status(500).json({ error: String(e.message || e) }) }
20
22
  })
21
- gui.route('GET', '/api/models/queues', (_, res) => res.json(getConfigValue('agent.model_queues', {}) || {}))
23
+ gui.route('GET', '/api/models/queues', (_, res) => {
24
+ const local = getConfigValue('agent.model_queues', {}) || {}
25
+ try {
26
+ const all = listAllQueues({ queuesMap: local })
27
+ const merged = { ...local }
28
+ for (const q of all) if (!merged[q.name]) merged[q.name] = q.links.map(m => ({ model: m, source: q.source }))
29
+ res.json(merged)
30
+ } catch { res.json(local) }
31
+ })
32
+ gui.route('GET', '/api/models/sampler/peek/:provider', (req, res) => {
33
+ try { res.json(peekStatus(req.params.provider)) }
34
+ catch (e) { res.status(500).json({ error: String(e.message || e) }) }
35
+ })
22
36
  gui.route('POST', '/api/models/queues', (req, res) => {
23
37
  const { name, entries } = req.body || {}
24
38
  if (!name || !Array.isArray(entries)) return res.status(400).json({ error: 'name and entries[] required' })
@@ -2,11 +2,9 @@ import { createRequire } from 'module'
2
2
  import { listAllProfiles } from '../../src/commands/profile.js'
3
3
  import { COMMAND_REGISTRY } from '../../src/commands/registry.js'
4
4
  import { getFreddieHome } from '../../src/home.js'
5
- import { PROVIDER_KEYS, DEFAULTS } from '../../src/agent/llm_resolver.js'
6
- import { getStatus } from '../../src/agent/model-sampler.js'
7
5
  import { resolveKey } from '../../src/agent/credential_sources.js'
8
6
  const _require = createRequire(import.meta.url)
9
- const { probeModels, getCachedModels } = _require('acptoapi')
7
+ const { probeModels, getCachedModels, PROVIDER_KEYS, PROVIDER_DEFAULTS: DEFAULTS, getStatus, peekStatus } = _require('acptoapi')
10
8
  export default {
11
9
  name: 'gui-profiles-commands-health', surfaces: 'gui',
12
10
  register({ gui }) {
@@ -1,6 +1,7 @@
1
1
  import { createRequire } from 'module'
2
2
  import { getConfigValue } from '../config.js'
3
3
  import { MATRIX_FILE } from './model-matrix.js'
4
+ import { callLLM as bridgeCall, isReachable as bridgeReachable } from './acptoapi-bridge.js'
4
5
  export { matrixUsable } from './model-matrix.js'
5
6
 
6
7
  const _require = createRequire(import.meta.url)
@@ -46,7 +47,12 @@ export function resolveCallLLM({ provider, model } = {}) {
46
47
  throw new Error('no LLM backend reachable: set a provider API key or start acptoapi (http://127.0.0.1:4800/v1)' + (status ? ' | sampler: ' + status : ''))
47
48
  }
48
49
  try {
49
- const r = await sdk.chat({ model: m, messages: toMsgs(input.messages), tools: toTools(input.tools), queuesMap: getConfigValue('agent.model_queues', {}) || {}, matrixSource: process.env.FREDDIE_MATRIX_URL || MATRIX_FILE, onFallback: input.onFallback, output: 'openai' })
50
+ const isSimple = typeof m === 'string' && !m.includes(',') && !/^queue\//.test(m)
51
+ if (isSimple && await bridgeReachable()) return await bridgeCall({ ...input, model: m })
52
+ const opts = { model: m, messages: toMsgs(input.messages), tools: toTools(input.tools), onFallback: input.onFallback, output: 'openai' }
53
+ if (/^queue\//.test(m)) opts.queuesMap = getConfigValue('agent.model_queues', {}) || {}
54
+ if (m.includes(',') || /^queue\//.test(m)) opts.matrixSource = process.env.FREDDIE_MATRIX_URL || MATRIX_FILE
55
+ const r = await sdk.chat(opts)
50
56
  return adapt(r)
51
57
  } catch (e) {
52
58
  if (/queue not found or empty/i.test(e.message)) throw e