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
37 changes: 36 additions & 1 deletion packages/core/data/providers/openai.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,41 @@
"systemMessage": true
},
"models": [
{
"id": "gpt-5.4",
"name": "GPT-5.4",
"contextWindow": 1050000,
"maxOutput": 128000,
"inputPrice": 2.5,
"outputPrice": 15,
"capabilities": [
"chat",
"vision",
"function_calling",
"json_mode",
"reasoning",
"streaming"
],
"default": true,
"releaseDate": "2026-03-05"
},
{
"id": "gpt-5.4-pro",
"name": "GPT-5.4 Pro",
"contextWindow": 1050000,
"maxOutput": 128000,
"inputPrice": 30,
"outputPrice": 180,
"capabilities": [
"chat",
"vision",
"function_calling",
"reasoning",
"streaming"
],
"default": false,
"releaseDate": "2026-03-05"
},
{
"id": "gpt-5.3-codex",
"name": "GPT-5.3 Codex",
Expand All @@ -26,7 +61,7 @@
"reasoning",
"streaming"
],
"default": true,
"default": false,
"releaseDate": "2026-02-05"
},
{
Expand Down
64 changes: 16 additions & 48 deletions packages/gateway/src/routes/agent-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ import {
injectMemoryIntoPrompt,
unsafeToolId,
getBaseName,
createProvider,
createFallbackProvider,
type ProviderConfig,
buildSoulPrompt,
} from '@ownpilot/core';
import type { SessionInfo } from '../types/index.js';
Expand Down Expand Up @@ -81,6 +78,7 @@ import {
MAX_AGENT_CACHE_SIZE,
MAX_CHAT_AGENT_CACHE_SIZE,
} from './agent-cache.js';
import { createRuntimeProvider, isCliRuntimeProvider } from '../services/model-execution.js';
import {
AGENT_DEFAULT_MAX_TOKENS,
AGENT_DEFAULT_TEMPERATURE,
Expand Down Expand Up @@ -115,7 +113,7 @@ async function createAgentFromRecord(record: AgentRecord): Promise<Agent> {
}

const apiKey = await getProviderApiKey(resolvedProvider);
if (!apiKey) {
if (!apiKey && !isCliRuntimeProvider(resolvedProvider)) {
throw new Error(`API key not configured for provider: ${resolvedProvider}`);
}

Expand Down Expand Up @@ -249,7 +247,7 @@ async function createAgentFromRecord(record: AgentRecord): Promise<Agent> {
systemPrompt: enhancedPrompt,
provider: {
provider: providerType as AIProvider,
apiKey,
apiKey: apiKey ?? '',
baseUrl,
headers: providerConfig?.headers,
},
Expand All @@ -264,7 +262,8 @@ async function createAgentFromRecord(record: AgentRecord): Promise<Agent> {
requestApproval: approvalCallback,
};

const agent = createAgent(config, { tools });
const providerInstance = await createRuntimeProvider(resolvedProvider);
const agent = createAgent(config, { tools, provider: providerInstance ?? undefined });

// Evict oldest entry if cache is at capacity
if (agentCache.size >= MAX_AGENT_CACHE_SIZE) {
Expand Down Expand Up @@ -426,7 +425,7 @@ async function createChatAgentInstance(
fallback?: { provider: string; model: string }
): Promise<Agent> {
const apiKey = await getProviderApiKey(provider);
if (!apiKey) {
if (!apiKey && !isCliRuntimeProvider(provider)) {
throw new Error(`API key not configured for provider: ${provider}`);
}

Expand Down Expand Up @@ -506,7 +505,7 @@ async function createChatAgentInstance(
systemPrompt: enhancedPrompt,
provider: {
provider: providerType as AIProvider,
apiKey,
apiKey: apiKey ?? '',
baseUrl,
headers: providerConfig?.headers,
},
Expand All @@ -522,37 +521,11 @@ async function createChatAgentInstance(
memory: { maxTokens: memoryMaxTokens },
};

// Build FallbackProvider if a backup model is configured
let providerInstance: IProvider | undefined;
if (fallback) {
try {
const fbApiKey = await getProviderApiKey(fallback.provider);
if (fbApiKey) {
const fbConfig = loadProviderConfig(fallback.provider);
const fbType = NATIVE_PROVIDERS.has(fallback.provider) ? fallback.provider : 'openai';
providerInstance = createFallbackProvider({
primary: {
provider: providerType as AIProvider,
apiKey,
baseUrl,
headers: providerConfig?.headers,
},
fallbacks: [
{
provider: fbType as AIProvider,
apiKey: fbApiKey,
baseUrl: fbConfig?.baseUrl,
headers: fbConfig?.headers,
},
],
onFallback: (failed, error, next) => {
log.warn(`Fallback triggered: ${String(failed)} -> ${String(next)}: ${error.message}`);
},
});
}
} catch (fbErr) {
log.warn(`Failed to build fallback provider: ${String(fbErr)}`);
}
try {
providerInstance = (await createRuntimeProvider(provider, fallback?.provider)) ?? undefined;
} catch (fbErr) {
log.warn(`Failed to build runtime provider: ${String(fbErr)}`);
}

if (chatAgentCache.size >= MAX_CHAT_AGENT_CACHE_SIZE) {
Expand Down Expand Up @@ -732,20 +705,15 @@ export async function compactContext(
const summaryPrompt = `Summarize the following conversation history into a concise summary (max 200 words). Focus on key topics discussed, decisions made, and important context needed to continue the conversation naturally:\n\n${conversationText}`;

const apiKey = await getProviderApiKey(provider);
if (!apiKey) {
if (!apiKey && !isCliRuntimeProvider(provider)) {
return { compacted: false, removedMessages: 0, newTokenEstimate: 0 };
}

const providerConfig = loadProviderConfig(provider);
const providerType = NATIVE_PROVIDERS.has(provider) ? provider : 'openai';

try {
const summaryProvider = createProvider({
provider: providerType as ProviderConfig['provider'],
apiKey,
baseUrl: providerConfig?.baseUrl,
headers: providerConfig?.headers,
});
const summaryProvider = await createRuntimeProvider(provider);
if (!summaryProvider) {
return { compacted: false, removedMessages: 0, newTokenEstimate: 0 };
}

const result = await summaryProvider.complete({
messages: [{ role: 'user', content: summaryPrompt }],
Expand Down
62 changes: 7 additions & 55 deletions packages/gateway/src/routes/extensions/audit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,7 @@
*/

import { Hono } from 'hono';
import {
createProvider,
getProviderConfig as coreGetProviderConfig,
getServiceRegistry,
Services,
type AIProvider,
} from '@ownpilot/core';
import { getServiceRegistry, Services } from '@ownpilot/core';
import type { ExtensionService } from '../../services/extension-service.js';
import type { ExtensionManifest } from '../../services/extension-types.js';
import {
Expand All @@ -33,28 +27,13 @@ import {
getErrorMessage,
parseJsonBody,
} from '../helpers.js';
import { resolveProviderAndModel, getApiKey } from '../settings.js';
import { localProvidersRepo } from '../../db/repositories/index.js';
import { resolveRuntimeProvider } from '../../services/model-execution.js';
import { getLog } from '../../services/log.js';

const log = getLog('ExtensionAudit');

export const auditRoutes = new Hono();

/** Providers with native SDK support (others use OpenAI-compatible) */
const NATIVE_PROVIDERS = new Set([
'openai',
'anthropic',
'google',
'deepseek',
'groq',
'mistral',
'xai',
'together',
'fireworks',
'perplexity',
]);

const getExtService = () => getServiceRegistry().get(Services.Extension) as ExtensionService;

// =============================================================================
Expand Down Expand Up @@ -139,45 +118,18 @@ async function runLlmAudit(
modelOverride?: string
): Promise<{ result: SkillLlmAuditResult | null; error: string | null }> {
try {
// 1. Resolve provider/model
const resolved = await resolveProviderAndModel(
providerOverride ?? 'default',
modelOverride ?? 'default'
);
if (!resolved.provider || !resolved.model) {
const resolved = await resolveRuntimeProvider(providerOverride, modelOverride);
if (!resolved.providerId || !resolved.model || !resolved.instance) {
return {
result: null,
error: 'No AI provider configured. Set up a provider in Settings for LLM analysis.',
};
}

// 2. Get API key
const localProv = await localProvidersRepo.getProvider(resolved.provider);
const apiKey = localProv
? localProv.apiKey || 'local-no-key'
: await getApiKey(resolved.provider);
if (!apiKey) {
return {
result: null,
error: `API key not configured for provider: ${resolved.provider}`,
};
}

// 3. Create provider instance
const providerConfig = coreGetProviderConfig(resolved.provider);
const providerType = NATIVE_PROVIDERS.has(resolved.provider) ? resolved.provider : 'openai';

const providerInstance = createProvider({
provider: providerType as AIProvider,
apiKey,
baseUrl: providerConfig?.baseUrl,
headers: providerConfig?.headers,
});

// 4. Build prompt and call LLM
// 2. Build prompt and call LLM
const prompt = buildLlmAuditPrompt(manifest, staticResult);

const result = await providerInstance.complete({
const result = await resolved.instance.complete({
model: { model: resolved.model, maxTokens: 4096, temperature: 0.3 },
messages: [{ role: 'user' as const, content: prompt }],
});
Expand All @@ -194,7 +146,7 @@ async function runLlmAudit(
return { result: null, error: 'LLM returned empty response' };
}

// 5. Parse structured response
// 3. Parse structured response
const auditResult = parseLlmAuditResponse(text);

log.info('LLM audit completed', {
Expand Down
48 changes: 4 additions & 44 deletions packages/gateway/src/routes/extensions/eval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@

import { Hono } from 'hono';
import {
createProvider,
getProviderConfig as coreGetProviderConfig,
getServiceRegistry,
Services,
type AIProvider,
} from '@ownpilot/core';
import type { ExtensionService } from '../../services/extension-service.js';
import {
Expand All @@ -24,62 +21,25 @@ import {
getErrorMessage,
parseJsonBody,
} from '../helpers.js';
import { resolveProviderAndModel, getApiKey } from '../settings.js';
import { localProvidersRepo } from '../../db/repositories/index.js';
import { resolveRuntimeProvider } from '../../services/model-execution.js';
import { getLog } from '../../services/log.js';

const log = getLog('ExtensionEval');

export const evalRoutes = new Hono();

/** Providers with native SDK support */
const NATIVE_PROVIDERS = new Set([
'openai',
'anthropic',
'google',
'deepseek',
'groq',
'mistral',
'xai',
'together',
'fireworks',
'perplexity',
]);

const getExtService = () => getServiceRegistry().get(Services.Extension) as ExtensionService;

// ---------------------------------------------------------------------------
// Shared: create a provider instance
// ---------------------------------------------------------------------------

async function buildProvider(providerOverride?: string, modelOverride?: string) {
const resolved = await resolveProviderAndModel(
providerOverride ?? 'default',
modelOverride ?? 'default'
);
if (!resolved.provider || !resolved.model) {
const resolved = await resolveRuntimeProvider(providerOverride, modelOverride);
if (!resolved.providerId || !resolved.model || !resolved.instance) {
return { provider: null, model: null, error: 'No AI provider configured.' };
}
const localProv = await localProvidersRepo.getProvider(resolved.provider);
const apiKey = localProv
? localProv.apiKey || 'local-no-key'
: await getApiKey(resolved.provider);
if (!apiKey) {
return {
provider: null,
model: null,
error: `API key not configured for: ${resolved.provider}`,
};
}
const providerConfig = coreGetProviderConfig(resolved.provider);
const providerType = NATIVE_PROVIDERS.has(resolved.provider) ? resolved.provider : 'openai';
const instance = createProvider({
provider: providerType as AIProvider,
apiKey,
baseUrl: providerConfig?.baseUrl,
headers: providerConfig?.headers,
});
return { provider: instance, model: resolved.model, error: null };
return { provider: resolved.instance, model: resolved.model, error: null };
}

// ---------------------------------------------------------------------------
Expand Down
Loading