capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
127 lines • 4.74 kB
JavaScript
import { BaseProvider } from './base.js';
import { openRouterModelsService } from '../services/openrouter-models.js';
export class ProxyProvider extends BaseProvider {
name;
models = [];
supportsStreaming = true;
supportsTools = true;
authToken;
providerName;
constructor(providerName, authToken, baseUrl) {
super('proxy', baseUrl || process.env.CAPSULE_API_URL || 'https://api.capsule.dev');
this.providerName = providerName;
this.name = providerName;
this.authToken = authToken;
this.loadProviderCapabilities();
}
async loadProviderCapabilities() {
await openRouterModelsService.fetchModels();
const models = openRouterModelsService.getModelsByProvider(this.providerName);
this.models = models.map(m => m.id);
if (this.models.length === 0) {
const fallbackModels = {
openai: ['openai/gpt-4o', 'openai/gpt-4o-mini'],
anthropic: ['anthropic/claude-opus-4', 'anthropic/claude-sonnet-4'],
google: ['google/gemini-2.5-pro', 'google/gemini-2.5-flash'],
};
this.models = fallbackModels[this.providerName] || [];
}
}
async complete(messages, options) {
const response = await fetch(`${this.baseUrl}/api/proxy/complete`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.authToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
provider: this.providerName,
model: options?.model || this.models[0],
messages,
options
}),
});
if (!response.ok) {
if (response.status === 402) {
const error = await response.json();
throw new Error(`Insufficient RUs: ${error.message || 'Please add more RUs or use your own API keys'}`);
}
else if (response.status === 401) {
throw new Error('Authentication expired. Please run `capsule auth <email>` again.');
}
const error = await response.text();
throw new Error(`Proxy request failed: ${error}`);
}
return response.json();
}
async *stream(messages, options) {
const response = await fetch(`${this.baseUrl}/api/proxy/stream`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.authToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
provider: this.providerName,
model: options?.model || this.models[0],
messages,
options
}),
});
if (!response.ok) {
if (response.status === 402) {
const error = await response.json();
throw new Error(`Insufficient RUs: ${error.message || 'Please add more RUs or use your own API keys'}`);
}
else if (response.status === 401) {
throw new Error('Authentication expired. Please run `capsule auth <email>` again.');
}
const error = await response.text();
throw new Error(`Proxy request failed: ${error}`);
}
const reader = response.body?.getReader();
if (!reader) {
throw new Error('No response body');
}
const decoder = new TextDecoder();
let buffer = '';
try {
while (true) {
const { done, value } = await reader.read();
if (done)
break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') {
return;
}
try {
const chunk = JSON.parse(data);
yield chunk;
}
catch (e) {
}
}
}
}
}
finally {
reader.releaseLock();
}
}
calculateCost(_usage, _model) {
return {
amount: 0,
currency: 'RU',
breakdown: {
prompt: 0,
completion: 0
}
};
}
}
//# sourceMappingURL=proxy.js.map