● Zion Boggan
repos/Zion Pm/lib/workers/delegate.ts
zionboggan.com ↗
55 lines · typescript
History for this file →
1
import type { DelegateParams, DelegateResult, ModelId } from './types';
2
import { claudeAdapter } from './claude';
3
import { codexAdapter } from './codex';
4
import { ollamaAdapter } from './ollama';
5
import { getCap, touchCap, addMessage } from '../db';
6
 
7
const TIER: ModelId[] = ['claude', 'codex', 'ollama'];
8
 
9
const ADAPTERS: Record<Exclude<ModelId, 'glm'>, (p: DelegateParams) => Promise<DelegateResult>> = {
10
  claude: claudeAdapter,
11
  codex: codexAdapter,
12
  ollama: ollamaAdapter,
13
};
14
 
15
function nextAvailable(from: ModelId): ModelId | null {
16
  const startIdx = Math.max(0, TIER.indexOf(from));
17
  for (let i = startIdx; i < TIER.length; i++) {
18
    const m = TIER[i];
19
    const cap = getCap(m);
20
    if (cap && cap.available === 1) return m;
21
  }
22
  return null;
23
}
24
 
25
 
26
export async function delegate(
27
  params: DelegateParams,
28
  opts: { channelId?: string; allowFallback?: boolean } = {}
29
): Promise<DelegateResult & { model: ModelId }> {
30
  if (params.model === 'glm') {
31
    return { ok: false, output: '', artifacts: [], error: 'glm is the conductor, not a worker', durationMs: 0, model: 'glm' };
32
  }
33
 
34
  let model: ModelId = params.model;
35
  const cap = getCap(model);
36
  if (!cap || cap.available !== 1) {
37
    if (opts.allowFallback === false) {
38
      return { ok: false, output: '', artifacts: [], error: `${model} unavailable and fallback disabled`, durationMs: 0, model };
39
    }
40
    const fb = nextAvailable(model);
41
    if (!fb) {
42
      return { ok: false, output: '', artifacts: [], error: 'no available worker in tier', durationMs: 0, model };
43
    }
44
    model = fb;
45
  }
46
 
47
  const adapter = ADAPTERS[model as Exclude<ModelId, 'glm'>];
48
  const result = await adapter({ ...params, model });
49
  touchCap(model);
50
 
51
  if (opts.channelId && result.output) {
52
    addMessage(opts.channelId, 'assistant', result.output, model);
53
  }
54
  return { ...result, model };
55
}