giga-code
Version:
A personal AI CLI assistant powered by Grok for local development.
514 lines • 24.9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GigaClient = void 0;
const openai_1 = __importDefault(require("openai"));
const cerebras_cloud_sdk_1 = __importDefault(require("@cerebras/cerebras_cloud_sdk"));
const session_manager_1 = require("../utils/session-manager");
const added_models_1 = require("../utils/added-models");
const token_counter_1 = require("../utils/token-counter");
class GigaClient {
constructor(apiKey, model, groqApiKey, anthropicApiKey, openRouterApiKey, googleApiKey, cerebrasApiKey, openaiApiKey, ollamaBaseUrl) {
if (apiKey) {
this.xaiClient = new openai_1.default({
apiKey,
baseURL: 'https://api.x.ai/v1',
timeout: 360000,
});
}
this.groqApiKey = groqApiKey;
if (groqApiKey) {
this.groqClient = new openai_1.default({
apiKey: groqApiKey,
baseURL: 'https://api.groq.com/openai/v1',
timeout: 360000,
});
}
this.anthropicApiKey = anthropicApiKey;
if (anthropicApiKey) {
this.anthropicClient = new openai_1.default({
apiKey: anthropicApiKey,
baseURL: 'https://api.anthropic.com/v1',
timeout: 360000,
});
}
this.openRouterApiKey = openRouterApiKey;
if (openRouterApiKey) {
this.openRouterClient = new openai_1.default({
apiKey: openRouterApiKey,
baseURL: 'https://openrouter.ai/api/v1',
timeout: 360000
});
}
this.googleApiKey = googleApiKey;
if (googleApiKey) {
this.googleClient = new openai_1.default({
apiKey: googleApiKey,
baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai',
timeout: 360000,
});
}
this.cerebrasApiKey = cerebrasApiKey;
if (cerebrasApiKey) {
this.cerebrasClient = new cerebras_cloud_sdk_1.default({
apiKey: cerebrasApiKey,
timeout: 360000,
});
}
this.openaiApiKey = openaiApiKey;
if (openaiApiKey) {
this.openaiClient = new openai_1.default({
apiKey: openaiApiKey,
baseURL: 'https://api.openai.com/v1',
timeout: 360000,
});
}
this.ollamaBaseUrl = ollamaBaseUrl || 'http://localhost:11434';
// Ensure Ollama base URL has proper protocol
let cleanOllamaUrl = this.ollamaBaseUrl;
if (!cleanOllamaUrl.startsWith('http://') && !cleanOllamaUrl.startsWith('https://')) {
cleanOllamaUrl = `http://${cleanOllamaUrl}`;
}
this.ollamaClient = new openai_1.default({
apiKey: 'ollama',
baseURL: cleanOllamaUrl + '/v1',
timeout: 360000,
});
if (model) {
session_manager_1.sessionManager.setCurrentModel(model);
}
}
getClientForModel(model) {
// First check added models to get the correct provider
const addedModels = require('../utils/added-models').loadAddedModels();
const addedModel = addedModels.find((m) => m.modelName === model);
if (addedModel) {
const providerName = addedModel.providerName.toLowerCase();
switch (providerName) {
case 'openrouter':
if (!this.openRouterClient) {
throw new Error('OpenRouter API key not provided. Please configure it in /providers.');
}
return this.openRouterClient;
case 'anthropic':
if (!this.anthropicClient) {
throw new Error('Anthropic API key not provided. Please configure it in /providers.');
}
return this.anthropicClient;
case 'google':
if (!this.googleClient) {
throw new Error('Google API key not provided. Please configure it in /providers.');
}
return this.googleClient;
case 'xai':
if (!this.xaiClient) {
throw new Error('XAI API key not provided. Please configure it in /providers.');
}
return this.xaiClient;
case 'groq':
if (!this.groqClient) {
throw new Error('Groq API key not provided. Please configure it in /providers.');
}
return this.groqClient;
case 'cerebras':
if (!this.cerebrasClient) {
throw new Error('Cerebras API key not provided. Please configure it in /providers.');
}
return this.cerebrasClient;
case 'openai':
if (!this.openaiClient) {
throw new Error('OpenAI API key not provided. Please configure it in /providers.');
}
return this.openaiClient;
case 'ollama':
return this.ollamaClient;
}
}
// OpenRouter models (access to multiple providers through one API)
const openRouterModels = [
'qwen/qwen3-235b-a22b-07-25',
'openai/gpt-4.1',
'qwen/qwen3-coder',
'deepseek/deepseek-r1-0528',
'deepseek/deepseek-chat',
'deepseek/deepseek-coder',
'z-ai/glm-4.5',
'meta-llama/llama-3.2-1b-instruct',
'anthropic/claude-3.5-sonnet',
'openai/gpt-4o',
'openai/gpt-3.5-turbo'
];
// Anthropic models
const anthropicModels = [
'claude-sonnet-4-20250514',
'claude-3-5-sonnet-20241022',
'claude-3-opus-20240229'
];
// Google models
const googleModels = [
'gemini-2.0-flash',
'gemini-2.5-flash',
'gemini-1.5-pro'
];
// xAI models (Grok)
const xaiModels = [
'grok-4-latest',
'grok-3-latest',
'grok-3-fast',
'grok-3-mini-fast',
'grok-beta'
];
// Groq models
const groqModels = [
'moonshotai/kimi-k2-instruct',
'llama-3.3-70b-versatile',
'llama3-8b-8192',
'llama3-70b-8192',
'llama-3.1-8b-instant',
'gemma2-9b-it'
];
// Cerebras models
const cerebrasModels = [
'llama3.1-8b',
'llama-4-scout-17b-16e-instruct',
'llama-3.3-70b',
'qwen-3-32b',
'qwen-3-235b-a22b-instruct-2507'
];
// OpenAI models
const openaiModels = [
'gpt-4o',
'gpt-4o-mini',
'gpt-3.5-turbo',
'gpt-4'
];
// Ollama models (dynamic - check by model format or known models)
const isOllamaModel = (modelName) => {
// Check if it's a known Ollama model format or common Ollama models
const commonOllamaModels = [
'llama2', 'llama2:7b', 'llama2:13b', 'llama2:70b',
'llama3', 'llama3:8b', 'llama3:70b', 'llama3.1', 'llama3.1:8b', 'llama3.1:70b', 'llama3.1:405b',
'llama3.2', 'llama3.2:3b', 'llama3.2:11b', 'llama3.2:90b',
'codellama', 'codellama:7b', 'codellama:13b', 'codellama:34b',
'mistral', 'mistral:7b', 'mistral:instruct',
'mixtral', 'mixtral:8x7b', 'mixtral:8x22b',
'qwen', 'qwen:4b', 'qwen:7b', 'qwen:14b', 'qwen:32b', 'qwen:72b',
'qwen2', 'qwen2:0.5b', 'qwen2:1.5b', 'qwen2:7b', 'qwen2:72b',
'qwen2.5', 'qwen2.5:0.5b', 'qwen2.5:1.5b', 'qwen2.5:3b', 'qwen2.5:7b', 'qwen2.5:14b', 'qwen2.5:32b', 'qwen2.5:72b',
'deepseek-coder', 'deepseek-coder:6.7b', 'deepseek-coder:33b',
'gemma', 'gemma:2b', 'gemma:7b', 'gemma2', 'gemma2:9b', 'gemma2:27b',
'phi', 'phi3', 'phi3:3.8b', 'phi3:14b',
'vicuna', 'vicuna:7b', 'vicuna:13b', 'vicuna:33b',
'orca-mini', 'orca-mini:3b', 'orca-mini:7b', 'orca-mini:13b',
'neural-chat', 'neural-chat:7b',
'starling-lm', 'starling-lm:7b',
'tinyllama', 'tinyllama:1.1b',
'wizard-vicuna-uncensored', 'wizard-vicuna-uncensored:7b', 'wizard-vicuna-uncensored:13b',
'nous-hermes', 'nous-hermes:7b', 'nous-hermes:13b', 'nous-hermes2',
'dolphin-mistral', 'dolphin-mistral:7b',
'solar', 'solar:10.7b'
];
if (commonOllamaModels.includes(modelName.toLowerCase())) {
return true;
}
// Check for Ollama model format patterns (model:tag or model/variant)
return /^[a-zA-Z0-9._-]+:[a-zA-Z0-9._-]+$/.test(modelName) ||
/^[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+$/.test(modelName) ||
// Simple heuristic: if it doesn't match other providers and contains certain patterns
(modelName.includes('llama') || modelName.includes('qwen') || modelName.includes('mistral') ||
modelName.includes('gemma') || modelName.includes('phi') || modelName.includes('deepseek')) &&
!openRouterModels.includes(modelName) &&
!anthropicModels.includes(modelName) &&
!googleModels.includes(modelName) &&
!xaiModels.includes(modelName) &&
!groqModels.includes(modelName) &&
!cerebrasModels.includes(modelName) &&
!openaiModels.includes(modelName);
};
if (openRouterModels.includes(model)) {
if (!this.openRouterClient) {
throw new Error('OpenRouter API key not provided. Please configure it in /providers.');
}
return this.openRouterClient;
}
else if (anthropicModels.includes(model)) {
if (!this.anthropicClient) {
throw new Error('Anthropic API key not provided. Please configure it in /providers.');
}
return this.anthropicClient;
}
else if (googleModels.includes(model)) {
if (!this.googleClient) {
throw new Error('Google API key not provided. Please configure it in /providers.');
}
return this.googleClient;
}
else if (xaiModels.includes(model)) {
if (!this.xaiClient) {
throw new Error('XAI API key not provided. Please configure it in /providers.');
}
return this.xaiClient;
}
else if (groqModels.includes(model)) {
if (!this.groqClient) {
throw new Error('Groq API key not provided. Please configure it in /providers.');
}
return this.groqClient;
}
else if (cerebrasModels.includes(model)) {
if (!this.cerebrasClient) {
throw new Error('Cerebras API key not provided. Please configure it in /providers.');
}
return this.cerebrasClient;
}
else if (openaiModels.includes(model)) {
if (!this.openaiClient) {
throw new Error('OpenAI API key not provided. Please configure it in /providers.');
}
return this.openaiClient;
}
else if (isOllamaModel(model)) {
return this.ollamaClient;
}
// Default to XAI for unknown models or grok models
if (!this.xaiClient) {
throw new Error('No suitable API client found for model. Please configure the appropriate API key in /providers.');
}
return this.xaiClient;
}
setModel(model) {
session_manager_1.sessionManager.setCurrentModel(model);
}
getCurrentModel() {
return session_manager_1.sessionManager.getCurrentModel();
}
getLastStreamingMetrics() {
return this.lastStreamingMetrics;
}
async chat(messages, tools, model) {
const startTime = Date.now();
const targetModel = model || session_manager_1.sessionManager.getCurrentModel();
try {
// Check if no model is configured
if (!targetModel) {
throw new Error('No model selected. Please configure a model first:\n\n1. Set up API keys: /providers\n2. Add models: /add-model\n3. Select a model: /models\n\nFor a quick start, try:\n• /providers → Add your API keys\n• /add-model → Add models from your providers\n• /models → Select the current model');
}
const tokenCounter = (0, token_counter_1.createTokenCounter)(targetModel);
const inputTokens = tokenCounter.countMessageTokens(messages);
const client = this.getClientForModel(targetModel);
// Check if this is a Cerebras client
if (client === this.cerebrasClient) {
const requestBody = {
model: targetModel,
messages,
temperature: session_manager_1.sessionManager.getTemperature(),
max_completion_tokens: 4000,
top_p: 0.95,
};
console.log(`DEBUG: Using Cerebras model: ${targetModel}`);
console.log(`DEBUG: Cerebras API Key present: ${this.cerebrasApiKey ? 'Yes' : 'No'}`);
const response = await client.chat.completions.create(requestBody);
const endTime = Date.now();
const durationMs = endTime - startTime;
// Calculate tokens and throughput for Cerebras
let outputTokens = 0;
if (response.usage?.completion_tokens) {
outputTokens = response.usage.completion_tokens;
}
else {
// Fallback: estimate from response content
const content = response.choices[0]?.message?.content || '';
outputTokens = tokenCounter.countTokens(content);
}
const totalTokens = inputTokens + outputTokens;
const outputTokensPerSecond = outputTokens / (durationMs / 1000);
// Add metrics to response (console display handled by streaming or agent)
// For non-streaming, estimate prefill as 30% of total time or max 1s
const prefillTime = Math.min(1000, Math.round(durationMs * 0.3));
const decodeTime = durationMs - prefillTime;
const decodeTokensPerSecond = decodeTime > 0 ? outputTokens / (decodeTime / 1000) : 0;
response.metrics = {
prefillTimeMs: prefillTime,
decodeTimeMs: decodeTime,
outputTokens: outputTokens,
tokensPerSecond: Math.round(decodeTokensPerSecond)
};
tokenCounter.dispose();
return response;
}
// Handle OpenAI-compatible clients
const openRouterProvider = (0, added_models_1.getOpenRouterProvider)(targetModel);
const requestBody = {
model: targetModel,
messages,
tools: tools || [],
tool_choice: tools ? 'auto' : undefined,
temperature: session_manager_1.sessionManager.getTemperature(),
max_tokens: 4000,
};
// If using OpenRouter client and have a provider preference, add provider routing
if (client === this.openRouterClient && openRouterProvider) {
requestBody.provider = {
order: [openRouterProvider],
allow_fallbacks: true
};
console.log(`DEBUG: Using preferred OpenRouter provider: ${openRouterProvider} for model: ${targetModel}`);
}
// Debug logging
console.log(`DEBUG: Using model: ${targetModel}`);
console.log(`DEBUG: Client type: ${client === this.openRouterClient ? 'OpenRouter' : client === this.groqClient ? 'Groq' : client === this.xaiClient ? 'XAI' : 'Other'}`);
console.log(`DEBUG: API Key present: ${this.openRouterApiKey ? 'Yes' : 'No'}`);
console.log(`DEBUG: Request payload:`, {
model: targetModel,
messages: messages.slice(0, 1),
provider: requestBody.provider,
});
const response = await client.chat.completions.create(requestBody);
const endTime = Date.now();
const durationMs = endTime - startTime;
// Calculate tokens and throughput
let outputTokens = 0;
if (response.usage?.completion_tokens) {
outputTokens = response.usage.completion_tokens;
}
else {
// Fallback: estimate from response content
const content = response.choices[0]?.message?.content || '';
outputTokens = tokenCounter.countTokens(content);
}
const totalTokens = inputTokens + outputTokens;
const outputTokensPerSecond = outputTokens / (durationMs / 1000);
// Add metrics to response (console display handled by streaming or agent)
// For non-streaming, estimate prefill as 30% of total time or max 1s
const prefillTime = Math.min(1000, Math.round(durationMs * 0.3));
const decodeTime = durationMs - prefillTime;
const decodeTokensPerSecond = decodeTime > 0 ? outputTokens / (decodeTime / 1000) : 0;
response.metrics = {
prefillTimeMs: prefillTime,
decodeTimeMs: decodeTime,
outputTokens: outputTokens,
tokensPerSecond: Math.round(decodeTokensPerSecond)
};
tokenCounter.dispose();
return response;
}
catch (error) {
const endTime = Date.now();
const durationMs = endTime - startTime;
console.log(`DEBUG: API Error for model ${targetModel} after ${(durationMs / 1000).toFixed(1)}s:`, error.message);
throw new Error(`API error: ${error.message}`);
}
}
async *chatStream(messages, tools, model) {
const startTime = Date.now();
const targetModel = model || session_manager_1.sessionManager.getCurrentModel();
let accumulatedContent = '';
let firstTokenTime = null;
try {
// Check if no model is configured
if (!targetModel) {
throw new Error('No model selected. Please configure a model first:\n\n1. Set up API keys: /providers\n2. Add models: /add-model\n3. Select a model: /models\n\nFor a quick start, try:\n• /providers → Add your API keys\n• /add-model → Add models from your providers\n• /models → Select the current model');
}
const tokenCounter = (0, token_counter_1.createTokenCounter)(targetModel);
const inputTokens = tokenCounter.countMessageTokens(messages);
const client = this.getClientForModel(targetModel);
// Check if this is a Cerebras client
if (client === this.cerebrasClient) {
const requestBody = {
model: targetModel,
messages,
temperature: session_manager_1.sessionManager.getTemperature(),
max_completion_tokens: 4000,
top_p: 0.95,
stream: true,
};
console.log(`DEBUG: Streaming with Cerebras model: ${targetModel}`);
const stream = await client.chat.completions.create(requestBody);
for await (const chunk of stream) {
if (chunk.choices?.[0]?.delta?.content) {
if (firstTokenTime === null) {
firstTokenTime = Date.now();
}
accumulatedContent += chunk.choices[0].delta.content;
}
yield chunk;
}
// Calculate and display metrics for Cerebras streaming
const endTime = Date.now();
const durationMs = endTime - startTime;
const outputTokens = tokenCounter.countTokens(accumulatedContent);
const totalTokens = inputTokens + outputTokens;
const outputTokensPerSecond = outputTokens / (durationMs / 1000);
// Calculate actual prefill and decode times
const prefillTime = firstTokenTime ? firstTokenTime - startTime : Math.round(durationMs * 0.3);
const decodeTime = firstTokenTime ? endTime - firstTokenTime : durationMs - prefillTime;
const decodeTokensPerSecond = decodeTime > 0 ? outputTokens / (decodeTime / 1000) : 0;
this.lastStreamingMetrics = {
prefillTimeMs: prefillTime,
decodeTimeMs: decodeTime,
outputTokens: outputTokens,
tokensPerSecond: Math.round(decodeTokensPerSecond)
};
tokenCounter.dispose();
return;
}
// Handle OpenAI-compatible clients
const openRouterProvider = (0, added_models_1.getOpenRouterProvider)(targetModel);
const requestBody = {
model: targetModel,
messages,
tools: tools || [],
tool_choice: tools ? 'auto' : undefined,
temperature: session_manager_1.sessionManager.getTemperature(),
max_tokens: 4000,
stream: true,
};
// If using OpenRouter client and have a provider preference, add provider routing
if (client === this.openRouterClient && openRouterProvider) {
requestBody.provider = {
order: [openRouterProvider],
allow_fallbacks: true
};
console.log(`DEBUG: Streaming with preferred OpenRouter provider: ${openRouterProvider} for model: ${targetModel}`);
}
const stream = await client.chat.completions.create(requestBody);
for await (const chunk of stream) {
if (chunk.choices?.[0]?.delta?.content) {
if (firstTokenTime === null) {
firstTokenTime = Date.now();
}
accumulatedContent += chunk.choices[0].delta.content;
}
yield chunk;
}
// Calculate and display metrics for streaming
const endTime = Date.now();
const durationMs = endTime - startTime;
const outputTokens = tokenCounter.countTokens(accumulatedContent);
const totalTokens = inputTokens + outputTokens;
const outputTokensPerSecond = outputTokens / (durationMs / 1000);
// Calculate actual prefill and decode times
const prefillTime = firstTokenTime ? firstTokenTime - startTime : Math.round(durationMs * 0.3);
const decodeTime = firstTokenTime ? endTime - firstTokenTime : durationMs - prefillTime;
const decodeTokensPerSecond = decodeTime > 0 ? outputTokens / (decodeTime / 1000) : 0;
console.log(`\x1b[34mprefill - ${prefillTime}ms\x1b[0m | \x1b[33mdecode - ${Math.round(decodeTokensPerSecond)} toks/sec (${outputTokens} out / ${decodeTime}ms)\x1b[0m`);
this.lastStreamingMetrics = {
prefillTimeMs: prefillTime,
decodeTimeMs: decodeTime,
outputTokens: outputTokens,
tokensPerSecond: Math.round(decodeTokensPerSecond)
};
tokenCounter.dispose();
}
catch (error) {
const endTime = Date.now();
const durationMs = endTime - startTime;
console.log(`DEBUG: Streaming API Error for model ${targetModel} after ${(durationMs / 1000).toFixed(1)}s:`, error.message);
throw new Error(`API error: ${error.message}`);
}
}
}
exports.GigaClient = GigaClient;
//# sourceMappingURL=client.js.map