Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ curl -X POST http://localhost:21100/api/v1/ingest \
| **DashScope** | qwen-plus, qwen-turbo | 通义千问, OpenAI-compatible |
| **Ollama** | qwen2.5, llama3.2 | Fully local, zero cost |
| **OpenRouter** | Any of 100+ models | Unified gateway |
| **Requesty** | Any of 300+ models | OpenAI-compatible gateway |

Each extraction/lifecycle LLM can be configured with a primary provider, an optional fallback provider, retry settings, and per-provider timeout.

Expand Down
15 changes: 15 additions & 0 deletions packages/dashboard/src/pages/Settings/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ export const LLM_PROVIDERS: Record<string, ProviderPreset> = {
],
envKey: 'OPENROUTER_API_KEY',
},
requesty: {
label: 'Requesty',
defaultBaseUrl: 'https://router.requesty.ai/v1',
models: [
'openai/gpt-4o-mini',
'openai/gpt-5.2',
'anthropic/claude-haiku-4-5',
'anthropic/claude-sonnet-4-6',
'google/gemini-2.5-flash',
'google/gemini-2.5-pro',
'deepseek/deepseek-chat',
'meta-llama/llama-4-maverick',
],
envKey: 'REQUESTY_API_KEY',
},
ollama: {
label: 'Ollama (Local)',
defaultBaseUrl: 'http://localhost:11434',
Expand Down
3 changes: 3 additions & 0 deletions packages/server/src/llm/cascade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AnthropicLLMProvider } from './anthropic.js';
import { OllamaLLMProvider } from './ollama.js';
import { GoogleLLMProvider } from './google.js';
import { OpenRouterLLMProvider } from './openrouter.js';
import { RequestyLLMProvider } from './requesty.js';
import { DeepSeekLLMProvider } from './deepseek.js';
import { normalizeLLMRetryConfig } from './config-utils.js';
import { createLogger } from '../utils/logger.js';
Expand Down Expand Up @@ -144,6 +145,8 @@ export function createLLMProvider(config: LLMProviderConfig): LLMProvider {
});
case 'openrouter':
return new OpenRouterLLMProvider(config);
case 'requesty':
return new RequestyLLMProvider(config);
case 'ollama':
return new OllamaLLMProvider(config);
case 'none':
Expand Down
58 changes: 58 additions & 0 deletions packages/server/src/llm/requesty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { LLMProvider, LLMCompletionOpts } from './interface.js';
import { createLogger } from '../utils/logger.js';

const log = createLogger('llm-requesty');

/**
* Requesty LLM Provider — routes to any model via Requesty's unified API.
* Uses OpenAI-compatible format.
*/
export class RequestyLLMProvider implements LLMProvider {
readonly name = 'requesty';
private apiKey: string;
private model: string;
private baseUrl: string;
private timeoutMs: number;

constructor(opts: { apiKey?: string; model?: string; baseUrl?: string; timeoutMs?: number }) {
this.apiKey = opts.apiKey || process.env.REQUESTY_API_KEY || '';
this.model = opts.model || 'openai/gpt-4o-mini';
this.baseUrl = (opts.baseUrl || 'https://router.requesty.ai/v1').replace(/\/+$/, '');
this.timeoutMs = opts.timeoutMs ?? 30000;
}

async complete(prompt: string, opts?: LLMCompletionOpts): Promise<string> {
if (!this.apiKey) throw new Error('Requesty API key not configured');

const messages: any[] = [];
if (opts?.systemPrompt) {
messages.push({ role: 'system', content: opts.systemPrompt });
}
messages.push({ role: 'user', content: prompt });

const res = await fetch(`${this.baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.apiKey}`,
'HTTP-Referer': 'https://github.com/rikouu/cortex',
'X-Title': 'Cortex Memory Service',
},
body: JSON.stringify({
model: this.model,
messages,
max_tokens: opts?.maxTokens || 500,
temperature: opts?.temperature ?? 0.3,
}),
signal: AbortSignal.timeout(this.timeoutMs),
});

if (!res.ok) {
const body = await res.text();
throw new Error(`Requesty API error ${res.status}: ${body}`);
}

const data = (await res.json()) as any;
return data.choices?.[0]?.message?.content || '';
}
}
2 changes: 1 addition & 1 deletion packages/server/src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const LLMRetrySchema = z.object({
});

const LLMProviderSchema = z.object({
provider: z.enum(['openai', 'anthropic', 'google', 'gemini', 'deepseek', 'dashscope', 'openrouter', 'ollama', 'none']),
provider: z.enum(['openai', 'anthropic', 'google', 'gemini', 'deepseek', 'dashscope', 'openrouter', 'requesty', 'ollama', 'none']),
model: z.string().optional(),
apiKey: z.string().optional(),
baseUrl: z.string().optional(),
Expand Down