claude-flow 3.6.26 → 3.6.27

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": "claude-flow",
3
- "version": "3.6.26",
3
+ "version": "3.6.27",
4
4
  "description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -11,6 +11,9 @@ const PROVIDER_CATALOG = [
11
11
  { name: 'OpenAI', type: 'LLM', models: 'gpt-4o, gpt-4-turbo', envVar: 'OPENAI_API_KEY', configName: 'openai' },
12
12
  { name: 'OpenAI', type: 'Embedding', models: 'text-embedding-3-small/large', envVar: 'OPENAI_API_KEY', configName: 'openai' },
13
13
  { name: 'Google', type: 'LLM', models: 'gemini-pro, gemini-ultra', envVar: 'GOOGLE_API_KEY', configName: 'google' },
14
+ // #1725: Ollama Cloud — Tier-2 default per ADR-026 (~$100/mo flat-rate alternative
15
+ // to per-token pricing). OpenAI-compat API at https://ollama.com/v1/chat/completions.
16
+ { name: 'Ollama', type: 'LLM', models: 'gpt-oss:120b-cloud, llama3:70b-cloud, qwen2.5-coder:32b-cloud', envVar: 'OLLAMA_API_KEY', configName: 'ollama' },
14
17
  { name: 'Transformers.js', type: 'Embedding', models: 'Xenova/all-MiniLM-L6-v2' },
15
18
  { name: 'Agentic Flow', type: 'Embedding', models: 'ONNX optimized' },
16
19
  { name: 'Mock', type: 'All', models: 'mock-*' },
@@ -30,6 +33,7 @@ function resolveApiKey(providerName, configuredProviders) {
30
33
  anthropic: 'ANTHROPIC_API_KEY',
31
34
  openai: 'OPENAI_API_KEY',
32
35
  google: 'GOOGLE_API_KEY',
36
+ ollama: 'OLLAMA_API_KEY', // #1725 — Tier-2 routing
33
37
  };
34
38
  const envVar = envMapping[providerName.toLowerCase()];
35
39
  if (envVar && process.env[envVar]) {
@@ -60,6 +64,13 @@ async function testProviderConnectivity(providerName, apiKey) {
60
64
  url: `https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`,
61
65
  headers: {},
62
66
  },
67
+ // #1725 — Ollama Cloud uses an OpenAI-compatible /v1 surface.
68
+ ollama: {
69
+ url: 'https://ollama.com/api/tags',
70
+ headers: {
71
+ 'Authorization': `Bearer ${apiKey}`,
72
+ },
73
+ },
63
74
  };
64
75
  const endpointConfig = endpoints[providerName.toLowerCase()];
65
76
  if (!endpointConfig) {
@@ -47,6 +47,12 @@ export interface AnthropicCallResult {
47
47
  * Generic Anthropic Messages API call. No agent registry coupling — used
48
48
  * by agent_execute (with the agent's configured model) and by the WASM
49
49
  * agent runtime (G4) when the bundled WASM only echoes input.
50
+ *
51
+ * #1725 — falls back to Ollama Cloud (Tier-2, OpenAI-compat) when
52
+ * ANTHROPIC_API_KEY is unset and OLLAMA_API_KEY is present, or when
53
+ * RUFLO_PROVIDER=ollama is explicitly set. Response shape is normalized
54
+ * to the Anthropic-flavored AnthropicCallResult so existing callers
55
+ * don't need to know which provider answered.
50
56
  */
51
57
  export declare function callAnthropicMessages(input: AnthropicCallInput): Promise<AnthropicCallResult>;
52
58
  /**
@@ -42,11 +42,26 @@ const MODEL_MAP = {
42
42
  * Generic Anthropic Messages API call. No agent registry coupling — used
43
43
  * by agent_execute (with the agent's configured model) and by the WASM
44
44
  * agent runtime (G4) when the bundled WASM only echoes input.
45
+ *
46
+ * #1725 — falls back to Ollama Cloud (Tier-2, OpenAI-compat) when
47
+ * ANTHROPIC_API_KEY is unset and OLLAMA_API_KEY is present, or when
48
+ * RUFLO_PROVIDER=ollama is explicitly set. Response shape is normalized
49
+ * to the Anthropic-flavored AnthropicCallResult so existing callers
50
+ * don't need to know which provider answered.
45
51
  */
46
52
  export async function callAnthropicMessages(input) {
47
- const apiKey = process.env.ANTHROPIC_API_KEY;
48
- if (!apiKey) {
49
- return { success: false, error: 'ANTHROPIC_API_KEY not set in environment' };
53
+ const explicitProvider = (process.env.RUFLO_PROVIDER || '').toLowerCase();
54
+ const ollamaKey = process.env.OLLAMA_API_KEY;
55
+ const anthropicKey = process.env.ANTHROPIC_API_KEY;
56
+ const useOllama = explicitProvider === 'ollama' || (!anthropicKey && !!ollamaKey);
57
+ if (useOllama && ollamaKey) {
58
+ return callOllamaCompat({ ...input, apiKey: ollamaKey });
59
+ }
60
+ if (!anthropicKey) {
61
+ return {
62
+ success: false,
63
+ error: 'No LLM provider configured. Set ANTHROPIC_API_KEY (Tier-3) or OLLAMA_API_KEY (Tier-2 Ollama Cloud — see issue #1725).',
64
+ };
50
65
  }
51
66
  const model = input.model || 'claude-3-5-sonnet-latest';
52
67
  const startedAt = Date.now();
@@ -56,7 +71,7 @@ export async function callAnthropicMessages(input) {
56
71
  const res = await fetch('https://api.anthropic.com/v1/messages', {
57
72
  method: 'POST',
58
73
  headers: {
59
- 'x-api-key': apiKey,
74
+ 'x-api-key': anthropicKey,
60
75
  'anthropic-version': '2023-06-01',
61
76
  'content-type': 'application/json',
62
77
  },
@@ -102,6 +117,98 @@ export async function callAnthropicMessages(input) {
102
117
  };
103
118
  }
104
119
  }
120
+ /**
121
+ * Ollama Cloud / OpenAI-compat provider — Tier-2 routing per ADR-026 + #1725.
122
+ *
123
+ * Endpoint: https://ollama.com/v1/chat/completions
124
+ * Auth: Authorization: Bearer <OLLAMA_API_KEY>
125
+ *
126
+ * Translates the Anthropic-flavored input shape onto OpenAI chat-completions
127
+ * and translates the response back so callers never see provider-specific
128
+ * fields. Logical model names are mapped to Ollama Cloud defaults:
129
+ * - 'haiku' / 'sonnet' → 'gpt-oss:120b-cloud' (sensible single default)
130
+ * - 'opus' → 'gpt-oss:120b-cloud' (no opus tier on Ollama)
131
+ * - explicit 'ollama:<model>' or bare provider-native name → passed through
132
+ */
133
+ async function callOllamaCompat(input) {
134
+ const model = resolveOllamaModel(input.model);
135
+ const startedAt = Date.now();
136
+ // OLLAMA_BASE_URL lets users point at local/self-hosted endpoints
137
+ // (e.g. http://ruvultra:11434, http://localhost:11434) instead of
138
+ // Ollama Cloud. Default is the public cloud endpoint.
139
+ const base = (process.env.OLLAMA_BASE_URL || 'https://ollama.com').replace(/\/+$/, '');
140
+ const url = `${base}/v1/chat/completions`;
141
+ // Self-hosted endpoints typically don't need an Authorization header
142
+ // (the daemon binds to 11434 with no auth by default), but Ollama Cloud
143
+ // does. Send the bearer when the key is non-empty AND looks cloud-shaped.
144
+ const sendAuth = input.apiKey && input.apiKey !== 'local';
145
+ try {
146
+ const controller = new AbortController();
147
+ const timer = setTimeout(() => controller.abort(), input.timeoutMs || 60000);
148
+ const res = await fetch(url, {
149
+ method: 'POST',
150
+ headers: {
151
+ ...(sendAuth ? { Authorization: `Bearer ${input.apiKey}` } : {}),
152
+ 'content-type': 'application/json',
153
+ },
154
+ body: JSON.stringify({
155
+ model,
156
+ max_tokens: input.maxTokens || 1024,
157
+ temperature: typeof input.temperature === 'number' ? input.temperature : 0.7,
158
+ messages: [
159
+ ...(input.systemPrompt
160
+ ? [{ role: 'system', content: input.systemPrompt }]
161
+ : []),
162
+ { role: 'user', content: input.prompt },
163
+ ],
164
+ }),
165
+ signal: controller.signal,
166
+ });
167
+ clearTimeout(timer);
168
+ if (!res.ok) {
169
+ const errText = await res.text().catch(() => '<unreadable error body>');
170
+ return { success: false, model, error: `Ollama API error ${res.status} at ${url}: ${errText.slice(0, 400)}` };
171
+ }
172
+ const data = (await res.json());
173
+ const textOut = data.choices?.[0]?.message?.content ?? '';
174
+ const usage = data.usage ?? {};
175
+ return {
176
+ success: true,
177
+ model: data.model ?? model,
178
+ messageId: data.id ?? `ollama-${Date.now()}`,
179
+ stopReason: data.choices?.[0]?.finish_reason ?? 'end_turn',
180
+ output: textOut,
181
+ usage: {
182
+ inputTokens: usage.prompt_tokens ?? 0,
183
+ outputTokens: usage.completion_tokens ?? 0,
184
+ totalTokens: usage.total_tokens ?? 0,
185
+ },
186
+ durationMs: Date.now() - startedAt,
187
+ };
188
+ }
189
+ catch (err) {
190
+ return {
191
+ success: false,
192
+ model,
193
+ error: err instanceof Error ? err.message : String(err),
194
+ durationMs: Date.now() - startedAt,
195
+ };
196
+ }
197
+ }
198
+ function resolveOllamaModel(input) {
199
+ const DEFAULT = 'gpt-oss:120b-cloud';
200
+ if (!input)
201
+ return DEFAULT;
202
+ // Logical → cloud default
203
+ if (input === 'haiku' || input === 'sonnet' || input === 'opus' || input === 'inherit') {
204
+ return DEFAULT;
205
+ }
206
+ // Explicit provider prefix
207
+ if (input.startsWith('ollama:'))
208
+ return input.slice('ollama:'.length);
209
+ // Bare name with cloud suffix (e.g. 'llama3:70b-cloud') passes through
210
+ return input;
211
+ }
105
212
  /**
106
213
  * Resolve a model identifier to an Anthropic model ID. Accepts:
107
214
  * - logical names: 'haiku', 'sonnet', 'opus', 'inherit'
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.6.26",
3
+ "version": "3.6.27",
4
4
  "type": "module",
5
5
  "description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",