@hemansubedi/aether-ai 1.0.0

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.
Files changed (90) hide show
  1. package/.gitattributes +3 -0
  2. package/.github/workflows/live-stats.yml +42 -0
  3. package/.github/workflows/publish.yml +34 -0
  4. package/.github/workflows/update-preview.yml +41 -0
  5. package/INSTALL.md +59 -0
  6. package/LICENSE +21 -0
  7. package/README.md +397 -0
  8. package/assets/aether-arena.svg +72 -0
  9. package/assets/aether-banner.svg +62 -0
  10. package/assets/aether-router.svg +129 -0
  11. package/dist/agent.js +125 -0
  12. package/dist/arena.js +486 -0
  13. package/dist/checkpoint.js +105 -0
  14. package/dist/client.js +95 -0
  15. package/dist/combos.js +176 -0
  16. package/dist/commands.js +483 -0
  17. package/dist/config.js +104 -0
  18. package/dist/cost.js +176 -0
  19. package/dist/git.js +52 -0
  20. package/dist/health.js +81 -0
  21. package/dist/index.js +272 -0
  22. package/dist/keys.js +128 -0
  23. package/dist/memory.js +98 -0
  24. package/dist/modes.js +68 -0
  25. package/dist/providers/index.js +32 -0
  26. package/dist/providers/ollama.js +206 -0
  27. package/dist/providers/openai-compat.js +181 -0
  28. package/dist/providers/openrouter.js +189 -0
  29. package/dist/providers/registry.js +211 -0
  30. package/dist/router-engine.js +200 -0
  31. package/dist/router.js +171 -0
  32. package/dist/server.js +210 -0
  33. package/dist/session.js +97 -0
  34. package/dist/settings.js +97 -0
  35. package/dist/skills.js +100 -0
  36. package/dist/tokensaver.js +50 -0
  37. package/dist/tools/filesystem.js +243 -0
  38. package/dist/tools/git.js +53 -0
  39. package/dist/tools/glob.js +175 -0
  40. package/dist/tools/grep.js +193 -0
  41. package/dist/tools/registry.js +39 -0
  42. package/dist/tools/vision.js +140 -0
  43. package/dist/tools/websearch.js +118 -0
  44. package/dist/tui.js +562 -0
  45. package/dist/types.js +8 -0
  46. package/docs/preview.txt +51 -0
  47. package/docs/screenshots.md +110 -0
  48. package/docs/stats.md +5 -0
  49. package/install.ps1 +170 -0
  50. package/install.sh +196 -0
  51. package/package.json +34 -0
  52. package/scripts/generate-stats-card.ts +62 -0
  53. package/scripts/patch_index.ps1 +17 -0
  54. package/scripts/release.sh +7 -0
  55. package/src/agent.ts +146 -0
  56. package/src/arena.ts +584 -0
  57. package/src/checkpoint.ts +111 -0
  58. package/src/client.ts +172 -0
  59. package/src/combos.ts +199 -0
  60. package/src/commands.ts +973 -0
  61. package/src/config.ts +122 -0
  62. package/src/cost.ts +206 -0
  63. package/src/git.ts +68 -0
  64. package/src/health.ts +90 -0
  65. package/src/index.ts +281 -0
  66. package/src/keys.ts +135 -0
  67. package/src/memory.ts +101 -0
  68. package/src/modes.ts +84 -0
  69. package/src/providers/index.ts +59 -0
  70. package/src/providers/ollama.ts +222 -0
  71. package/src/providers/openai-compat.ts +188 -0
  72. package/src/providers/openrouter.ts +198 -0
  73. package/src/providers/registry.ts +223 -0
  74. package/src/router-engine.ts +214 -0
  75. package/src/router.ts +195 -0
  76. package/src/server.ts +242 -0
  77. package/src/session.ts +111 -0
  78. package/src/settings.ts +125 -0
  79. package/src/skills.ts +106 -0
  80. package/src/tokensaver.ts +57 -0
  81. package/src/tools/filesystem.ts +258 -0
  82. package/src/tools/git.ts +53 -0
  83. package/src/tools/glob.ts +180 -0
  84. package/src/tools/grep.ts +192 -0
  85. package/src/tools/registry.ts +54 -0
  86. package/src/tools/vision.ts +152 -0
  87. package/src/tools/websearch.ts +130 -0
  88. package/src/tui.ts +664 -0
  89. package/src/types.ts +77 -0
  90. package/tsconfig.json +16 -0
@@ -0,0 +1,181 @@
1
+ import { fetchWithTimeout } from "./index.js";
2
+ export class OpenAICompatProvider {
3
+ name;
4
+ config;
5
+ constructor(config) {
6
+ this.name = config.name;
7
+ this.config = config;
8
+ }
9
+ get base() {
10
+ return this.config.baseURL.replace(/\/$/, "");
11
+ }
12
+ headers() {
13
+ const h = { "Content-Type": "application/json" };
14
+ if (this.config.apiKey) {
15
+ h.Authorization = `Bearer ${this.config.apiKey}`;
16
+ }
17
+ return h;
18
+ }
19
+ async listModels() {
20
+ try {
21
+ const res = await fetchWithTimeout(`${this.base}/models`, { headers: this.headers() }, this.config.timeoutMs);
22
+ if (!res.ok)
23
+ return [];
24
+ const data = (await res.json());
25
+ return (data?.data ?? []).map((m) => m.id).filter(Boolean);
26
+ }
27
+ catch {
28
+ return [];
29
+ }
30
+ }
31
+ async health() {
32
+ const start = Date.now();
33
+ try {
34
+ const res = await fetchWithTimeout(`${this.base}/models`, { headers: this.headers() }, 5000);
35
+ if (!res.ok) {
36
+ return { healthy: false, failures: 0, lastCheck: start, lastError: `HTTP ${res.status}`, circuitOpen: false, cooldownUntil: 0 };
37
+ }
38
+ return { healthy: true, failures: 0, lastCheck: start, circuitOpen: false, cooldownUntil: 0 };
39
+ }
40
+ catch (err) {
41
+ return { healthy: false, failures: 0, lastCheck: start, lastError: err.message, circuitOpen: false, cooldownUntil: 0 };
42
+ }
43
+ }
44
+ async *chat(messages, tools, opts) {
45
+ const body = {
46
+ model: this.config.models[0] || "gpt-4o-mini",
47
+ messages: messages.map((m) => ({
48
+ role: m.role,
49
+ content: m.content,
50
+ ...(m.tool_calls ? { tool_calls: m.tool_calls } : {}),
51
+ ...(m.tool_call_id ? { tool_call_id: m.tool_call_id } : {}),
52
+ ...(m.name ? { name: m.name } : {}),
53
+ })),
54
+ stream: true,
55
+ temperature: opts?.temperature ?? 0.7,
56
+ };
57
+ if (tools && tools.length > 0) {
58
+ body.tools = tools.map((t) => ({
59
+ type: "function",
60
+ function: { name: t.name, description: t.description, parameters: t.parameters },
61
+ }));
62
+ }
63
+ if (opts?.maxTokens) {
64
+ body.max_tokens = opts.maxTokens;
65
+ }
66
+ const res = await fetchWithTimeout(`${this.base}/chat/completions`, {
67
+ method: "POST",
68
+ headers: this.headers(),
69
+ body: JSON.stringify(body),
70
+ signal: opts?.signal,
71
+ }, this.config.timeoutMs);
72
+ if (!res.ok) {
73
+ const text = await res.text().catch(() => "");
74
+ throw new Error(`OpenAI-compatible chat failed: HTTP ${res.status} ${text}`);
75
+ }
76
+ const reader = res.body?.getReader();
77
+ if (!reader) {
78
+ throw new Error("OpenAI-compatible chat: no response body");
79
+ }
80
+ const decoder = new TextDecoder();
81
+ let buffer = "";
82
+ const toolParts = {};
83
+ let inputTokens = 0;
84
+ let outputTokens = 0;
85
+ let sawDone = false;
86
+ const emitToolCalls = () => {
87
+ const calls = [];
88
+ for (const idx of Object.keys(toolParts).map(Number).sort((a, b) => a - b)) {
89
+ const p = toolParts[idx];
90
+ if (!p)
91
+ continue;
92
+ calls.push({
93
+ id: p.id || `call_${idx}`,
94
+ type: "function",
95
+ function: { name: p.name, arguments: p.arguments },
96
+ });
97
+ }
98
+ Object.keys(toolParts).forEach((k) => delete toolParts[Number(k)]);
99
+ return calls.length ? { type: "tool_call", tool_call: calls[0] } : null;
100
+ };
101
+ while (true) {
102
+ const { done, value } = await reader.read();
103
+ if (done)
104
+ break;
105
+ buffer += decoder.decode(value, { stream: true });
106
+ let idx = buffer.indexOf("\n");
107
+ while (idx !== -1) {
108
+ const rawLine = buffer.slice(0, idx);
109
+ buffer = buffer.slice(idx + 1);
110
+ const line = rawLine.trim();
111
+ if (!line)
112
+ continue;
113
+ if (line === "data: [DONE]") {
114
+ sawDone = true;
115
+ continue;
116
+ }
117
+ if (!line.startsWith("data:"))
118
+ continue;
119
+ const payload = line.slice(5).trim();
120
+ if (!payload)
121
+ continue;
122
+ let obj;
123
+ try {
124
+ obj = JSON.parse(payload);
125
+ }
126
+ catch {
127
+ continue;
128
+ }
129
+ if (obj?.error) {
130
+ throw new Error(`OpenAI-compatible error: ${JSON.stringify(obj.error)}`);
131
+ }
132
+ const choice = obj?.choices?.[0];
133
+ if (!choice) {
134
+ if (obj?.usage) {
135
+ inputTokens = obj.usage.prompt_tokens ?? inputTokens;
136
+ outputTokens = obj.usage.completion_tokens ?? outputTokens;
137
+ }
138
+ continue;
139
+ }
140
+ const delta = choice.delta ?? {};
141
+ if (delta.content) {
142
+ yield { type: "text", text: delta.content };
143
+ }
144
+ if (Array.isArray(delta.tool_calls)) {
145
+ for (const tc of delta.tool_calls) {
146
+ const i = tc.index ?? 0;
147
+ if (!toolParts[i])
148
+ toolParts[i] = { name: "", arguments: "" };
149
+ if (tc.id)
150
+ toolParts[i].id = tc.id;
151
+ if (tc.function?.name)
152
+ toolParts[i].name = tc.function.name;
153
+ if (tc.function?.arguments)
154
+ toolParts[i].arguments += tc.function.arguments;
155
+ }
156
+ }
157
+ if (obj?.usage) {
158
+ inputTokens = obj.usage.prompt_tokens ?? inputTokens;
159
+ outputTokens = obj.usage.completion_tokens ?? outputTokens;
160
+ }
161
+ if (choice.finish_reason) {
162
+ const tc = emitToolCalls();
163
+ if (tc)
164
+ yield tc;
165
+ yield {
166
+ type: "done",
167
+ usage: { input_tokens: inputTokens, output_tokens: outputTokens },
168
+ };
169
+ sawDone = true;
170
+ }
171
+ idx = buffer.indexOf("\n");
172
+ }
173
+ }
174
+ if (!sawDone) {
175
+ const tc = emitToolCalls();
176
+ if (tc)
177
+ yield tc;
178
+ yield { type: "done", usage: { input_tokens: inputTokens, output_tokens: outputTokens } };
179
+ }
180
+ }
181
+ }
@@ -0,0 +1,189 @@
1
+ import { fetchWithTimeout } from "./index.js";
2
+ const FREE_MARKERS = ["/free", "free-", "-free", "zero", "lite", "nemo"];
3
+ function isFree(id) {
4
+ const lower = id.toLowerCase();
5
+ return FREE_MARKERS.some((m) => lower.includes(m));
6
+ }
7
+ export class OpenRouterProvider {
8
+ name;
9
+ config;
10
+ constructor(config) {
11
+ this.name = config.name;
12
+ this.config = config;
13
+ }
14
+ get base() {
15
+ return this.config.baseURL.replace(/\/$/, "");
16
+ }
17
+ headers() {
18
+ const h = { "Content-Type": "application/json" };
19
+ if (this.config.apiKey) {
20
+ h.Authorization = `Bearer ${this.config.apiKey}`;
21
+ }
22
+ h["HTTP-Referer"] = "https://github.com/kilocode/aether";
23
+ h["X-Title"] = "aether";
24
+ return h;
25
+ }
26
+ async listModels() {
27
+ try {
28
+ const res = await fetchWithTimeout(`${this.base}/models`, { headers: this.headers() }, this.config.timeoutMs);
29
+ if (!res.ok)
30
+ return [];
31
+ const data = (await res.json());
32
+ const all = (data?.data ?? []).map((m) => m.id).filter(Boolean);
33
+ return all.filter(isFree);
34
+ }
35
+ catch {
36
+ return [];
37
+ }
38
+ }
39
+ async health() {
40
+ const start = Date.now();
41
+ try {
42
+ const res = await fetchWithTimeout(`${this.base}/models`, { headers: this.headers() }, 5000);
43
+ if (!res.ok) {
44
+ return { healthy: false, failures: 0, lastCheck: start, lastError: `HTTP ${res.status}`, circuitOpen: false, cooldownUntil: 0 };
45
+ }
46
+ return { healthy: true, failures: 0, lastCheck: start, circuitOpen: false, cooldownUntil: 0 };
47
+ }
48
+ catch (err) {
49
+ return { healthy: false, failures: 0, lastCheck: start, lastError: err.message, circuitOpen: false, cooldownUntil: 0 };
50
+ }
51
+ }
52
+ async *chat(messages, tools, opts) {
53
+ const body = {
54
+ model: this.config.models[0] || "openrouter/auto",
55
+ messages: messages.map((m) => ({
56
+ role: m.role,
57
+ content: m.content,
58
+ ...(m.tool_calls ? { tool_calls: m.tool_calls } : {}),
59
+ ...(m.tool_call_id ? { tool_call_id: m.tool_call_id } : {}),
60
+ ...(m.name ? { name: m.name } : {}),
61
+ })),
62
+ stream: true,
63
+ temperature: opts?.temperature ?? 0.7,
64
+ };
65
+ if (tools && tools.length > 0) {
66
+ body.tools = tools.map((t) => ({
67
+ type: "function",
68
+ function: { name: t.name, description: t.description, parameters: t.parameters },
69
+ }));
70
+ }
71
+ if (opts?.maxTokens) {
72
+ body.max_tokens = opts.maxTokens;
73
+ }
74
+ const res = await fetchWithTimeout(`${this.base}/chat/completions`, {
75
+ method: "POST",
76
+ headers: this.headers(),
77
+ body: JSON.stringify(body),
78
+ signal: opts?.signal,
79
+ }, this.config.timeoutMs);
80
+ if (!res.ok) {
81
+ const text = await res.text().catch(() => "");
82
+ throw new Error(`OpenRouter chat failed: HTTP ${res.status} ${text}`);
83
+ }
84
+ const reader = res.body?.getReader();
85
+ if (!reader) {
86
+ throw new Error("OpenRouter chat: no response body");
87
+ }
88
+ const decoder = new TextDecoder();
89
+ let buffer = "";
90
+ const toolParts = {};
91
+ let inputTokens = 0;
92
+ let outputTokens = 0;
93
+ let sawDone = false;
94
+ const emitToolCalls = () => {
95
+ const calls = [];
96
+ for (const idx of Object.keys(toolParts).map(Number).sort((a, b) => a - b)) {
97
+ const p = toolParts[idx];
98
+ if (!p)
99
+ continue;
100
+ calls.push({
101
+ id: p.id || `call_${idx}`,
102
+ type: "function",
103
+ function: { name: p.name, arguments: p.arguments },
104
+ });
105
+ }
106
+ Object.keys(toolParts).forEach((k) => delete toolParts[Number(k)]);
107
+ return calls.length ? { type: "tool_call", tool_call: calls[0] } : null;
108
+ };
109
+ while (true) {
110
+ const { done, value } = await reader.read();
111
+ if (done)
112
+ break;
113
+ buffer += decoder.decode(value, { stream: true });
114
+ let idx = buffer.indexOf("\n");
115
+ while (idx !== -1) {
116
+ const rawLine = buffer.slice(0, idx);
117
+ buffer = buffer.slice(idx + 1);
118
+ const line = rawLine.trim();
119
+ if (!line)
120
+ continue;
121
+ if (line === "data: [DONE]") {
122
+ sawDone = true;
123
+ continue;
124
+ }
125
+ if (!line.startsWith("data:"))
126
+ continue;
127
+ const payload = line.slice(5).trim();
128
+ if (!payload)
129
+ continue;
130
+ let obj;
131
+ try {
132
+ obj = JSON.parse(payload);
133
+ }
134
+ catch {
135
+ continue;
136
+ }
137
+ if (obj?.error) {
138
+ throw new Error(`OpenRouter error: ${JSON.stringify(obj.error)}`);
139
+ }
140
+ const choice = obj?.choices?.[0];
141
+ if (!choice) {
142
+ if (obj?.usage) {
143
+ inputTokens = obj.usage.prompt_tokens ?? inputTokens;
144
+ outputTokens = obj.usage.completion_tokens ?? outputTokens;
145
+ }
146
+ continue;
147
+ }
148
+ const delta = choice.delta ?? {};
149
+ if (delta.content) {
150
+ yield { type: "text", text: delta.content };
151
+ }
152
+ if (Array.isArray(delta.tool_calls)) {
153
+ for (const tc of delta.tool_calls) {
154
+ const i = tc.index ?? 0;
155
+ if (!toolParts[i])
156
+ toolParts[i] = { name: "", arguments: "" };
157
+ if (tc.id)
158
+ toolParts[i].id = tc.id;
159
+ if (tc.function?.name)
160
+ toolParts[i].name = tc.function.name;
161
+ if (tc.function?.arguments)
162
+ toolParts[i].arguments += tc.function.arguments;
163
+ }
164
+ }
165
+ if (obj?.usage) {
166
+ inputTokens = obj.usage.prompt_tokens ?? inputTokens;
167
+ outputTokens = obj.usage.completion_tokens ?? outputTokens;
168
+ }
169
+ if (choice.finish_reason) {
170
+ const tc = emitToolCalls();
171
+ if (tc)
172
+ yield tc;
173
+ yield {
174
+ type: "done",
175
+ usage: { input_tokens: inputTokens, output_tokens: outputTokens },
176
+ };
177
+ sawDone = true;
178
+ }
179
+ idx = buffer.indexOf("\n");
180
+ }
181
+ }
182
+ if (!sawDone) {
183
+ const tc = emitToolCalls();
184
+ if (tc)
185
+ yield tc;
186
+ yield { type: "done", usage: { input_tokens: inputTokens, output_tokens: outputTokens } };
187
+ }
188
+ }
189
+ }
@@ -0,0 +1,211 @@
1
+ export const PROVIDER_REGISTRY = [
2
+ {
3
+ name: "ollama-local",
4
+ baseURL: "http://localhost:11434",
5
+ type: "ollama",
6
+ models: ["goekdenizguelmez/JOSIEFIED-Qwen3:8b", "hf.co/OBLITERATUS/Qwen3.8-27B-OBLITERATED:Q4_K_M", "llama3.1:8b", "qwen2.5:7b", "mistral:7b", "phi3:medium"],
7
+ priority: 1,
8
+ enabled: true,
9
+ maxRetries: 2,
10
+ timeoutMs: 120000,
11
+ },
12
+ {
13
+ name: "openrouter-free",
14
+ baseURL: "https://openrouter.ai/api/v1",
15
+ type: "openrouter",
16
+ models: [
17
+ "openrouter/auto",
18
+ "meta-llama/llama-3.1-8b-instruct:free",
19
+ "google/gemma-2-9b-it:free",
20
+ "mistralai/mistral-7b-instruct:free",
21
+ "nvidia/nemotron-70b-instruct:free",
22
+ ],
23
+ priority: 2,
24
+ enabled: true,
25
+ maxRetries: 3,
26
+ timeoutMs: 120000,
27
+ },
28
+ {
29
+ name: "openai-compatible",
30
+ baseURL: "",
31
+ type: "openai-compatible",
32
+ models: ["gpt-4o-mini"],
33
+ priority: 99,
34
+ enabled: false,
35
+ maxRetries: 3,
36
+ timeoutMs: 120000,
37
+ },
38
+ {
39
+ name: "groq",
40
+ baseURL: "https://api.groq.com/openai/v1",
41
+ type: "openai-compatible",
42
+ models: ["llama-3.1-8b-instant", "llama-3.1-70b-versatile", "llama-3.2-1b-preview", "llama-3.2-11b-vision-preview", "mixtral-8x7b-32768"],
43
+ priority: 3,
44
+ enabled: true,
45
+ maxRetries: 3,
46
+ timeoutMs: 120000,
47
+ },
48
+ {
49
+ name: "mistral",
50
+ baseURL: "https://api.mistral.com/v1",
51
+ type: "openai-compatible",
52
+ models: ["mistral-small-latest", "mistral-medium-latest", "mistral-large-latest", "mixtral-8x7b-latest"],
53
+ priority: 4,
54
+ enabled: true,
55
+ maxRetries: 3,
56
+ timeoutMs: 120000,
57
+ },
58
+ {
59
+ name: "cohere",
60
+ baseURL: "https://api.cohere.com/v1",
61
+ type: "openai-compatible",
62
+ models: ["command-r", "command-r-plus", "command-light"],
63
+ priority: 5,
64
+ enabled: true,
65
+ maxRetries: 3,
66
+ timeoutMs: 120000,
67
+ },
68
+ {
69
+ name: "huggingface",
70
+ baseURL: "https://api-inference.huggingface.co",
71
+ type: "openai-compatible",
72
+ models: ["HuggingFaceH4/zephyr-7b-beta", "HuggingFaceH4/mistral-7b-it", "bigcode/starcoder2-15b"],
73
+ priority: 6,
74
+ enabled: true,
75
+ maxRetries: 3,
76
+ timeoutMs: 120000,
77
+ },
78
+ {
79
+ name: "fireworks",
80
+ baseURL: "https://api.fireworks.ai/inference/v1",
81
+ type: "openai-compatible",
82
+ models: ["accounts/fireworks/models/llama-v3-8b-instruct", "accounts/fireworks/models/llama-v3-70b-instruct", "accounts/fireworks/models/mixtral-8x7b-instruct"],
83
+ priority: 7,
84
+ enabled: true,
85
+ maxRetries: 3,
86
+ timeoutMs: 120000,
87
+ },
88
+ {
89
+ name: "together",
90
+ baseURL: "https://api.together.xyz/v1",
91
+ type: "openai-compatible",
92
+ models: ["meta-llama/Llama-3-8b-hf-chat", "meta-llama/Llama-3-70b-hf-chat", "mistralai/Mixtral-8x7B-Instruct-v0.1", "Qwen/Qwen1.5-7B-Chat"],
93
+ priority: 8,
94
+ enabled: true,
95
+ maxRetries: 3,
96
+ timeoutMs: 120000,
97
+ },
98
+ {
99
+ name: "deepseek",
100
+ baseURL: "https://api.deepseek.com/v1",
101
+ type: "openai-compatible",
102
+ models: ["deepseek-chat", "deepseek-coder", "deepseek-reasoner"],
103
+ priority: 9,
104
+ enabled: true,
105
+ maxRetries: 3,
106
+ timeoutMs: 120000,
107
+ },
108
+ {
109
+ name: "gemini",
110
+ baseURL: "https://generativelanguage.googleapis.com/v1/openai",
111
+ type: "openai-compatible",
112
+ models: ["gemini-1.5-flash", "gemini-1.5-pro", "gemini-2.0-flash-exp"],
113
+ priority: 10,
114
+ enabled: true,
115
+ maxRetries: 3,
116
+ timeoutMs: 120000,
117
+ },
118
+ {
119
+ name: "xai",
120
+ baseURL: "https://api.x.ai/v1",
121
+ type: "openai-compatible",
122
+ models: ["grok-2", "grok-2-mini", "grok-beta", "grok-mini"],
123
+ priority: 11,
124
+ enabled: true,
125
+ maxRetries: 3,
126
+ timeoutMs: 120000,
127
+ },
128
+ {
129
+ name: "perplexity",
130
+ baseURL: "https://api.perplexity.ai",
131
+ type: "openai-compatible",
132
+ models: ["sonar", "sonar-pro", "sonar-reasoning", "sonar-deep-research"],
133
+ priority: 12,
134
+ enabled: true,
135
+ maxRetries: 3,
136
+ timeoutMs: 120000,
137
+ },
138
+ {
139
+ name: "cerebras",
140
+ baseURL: "https://api.cerebras.ai/v1",
141
+ type: "openai-compatible",
142
+ models: ["llama3.1-8b", "llama3.1-70b", "llama3.2-1b", "llama3.2-11b-vision", "qwen-32b"],
143
+ priority: 13,
144
+ enabled: true,
145
+ maxRetries: 3,
146
+ timeoutMs: 120000,
147
+ },
148
+ {
149
+ name: "cloudflare",
150
+ baseURL: "",
151
+ type: "openai-compatible",
152
+ models: ["@cf/meta-llama/llama-3.1-8b-instruct", "@cf/qwen/qwen2.5-7b-instruct", "@cf/mistral/mistral-7b-instruct"],
153
+ priority: 99,
154
+ enabled: false,
155
+ maxRetries: 3,
156
+ timeoutMs: 120000,
157
+ },
158
+ {
159
+ name: "nvidia",
160
+ baseURL: "https://integrate.api.nvidia.com/v1",
161
+ type: "openai-compatible",
162
+ models: ["meta/llama-3.1-8b-instruct", "meta/llama-3.1-70b-instruct", "mistralai/mistral-7b-instruct", "nvidia/nemotron-4-340b-instruct"],
163
+ priority: 14,
164
+ enabled: true,
165
+ maxRetries: 3,
166
+ timeoutMs: 120000,
167
+ },
168
+ {
169
+ name: "voyage",
170
+ baseURL: "",
171
+ type: "openai-compatible",
172
+ models: ["voyage-3-large", "voyage-3", "voyage-code-3"],
173
+ priority: 99,
174
+ enabled: false,
175
+ maxRetries: 3,
176
+ timeoutMs: 120000,
177
+ },
178
+ {
179
+ name: "jina",
180
+ baseURL: "https://api.jina.ai/v1",
181
+ type: "openai-compatible",
182
+ models: ["jina-embeddings-v3", "jina-clip-v2"],
183
+ priority: 15,
184
+ enabled: true,
185
+ maxRetries: 3,
186
+ timeoutMs: 120000,
187
+ },
188
+ {
189
+ name: "parasail",
190
+ baseURL: "https://api.parasail.ai/v1",
191
+ type: "openai-compatible",
192
+ models: ["parasail-dqn-turbo-70b", "parasail-mixtral-8x7b", "parasail-llama-3-8b"],
193
+ priority: 16,
194
+ enabled: true,
195
+ maxRetries: 3,
196
+ timeoutMs: 120000,
197
+ },
198
+ {
199
+ name: "featherless",
200
+ baseURL: "https://api.featherless.ai/v1",
201
+ type: "openai-compatible",
202
+ models: ["featherless/qwen-2.5-7b-instruct", "featherless/llama-3.1-8b-instruct", "featherless/mistral-7b-instruct"],
203
+ priority: 17,
204
+ enabled: true,
205
+ maxRetries: 3,
206
+ timeoutMs: 120000,
207
+ },
208
+ ];
209
+ export function getProvider(name) {
210
+ return PROVIDER_REGISTRY.find((p) => p.name === name);
211
+ }