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
8 changes: 4 additions & 4 deletions agentic-flow/src/agents/claudeAgentDirect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,28 @@ function getModelForProvider(provider: string): {
return {
model: envModel || 'gemini-2.0-flash-exp',
apiKey: process.env.GOOGLE_GEMINI_API_KEY || '',
baseURL: process.env.GEMINI_PROXY_URL || 'http://localhost:3000'
baseURL: process.env.GEMINI_PROXY_URL || `http://localhost:${process.env.PROXY_PORT || '3000'}`
};

case 'requesty':
return {
model: envModel || 'deepseek/deepseek-chat',
apiKey: process.env.REQUESTY_API_KEY || '',
baseURL: process.env.REQUESTY_PROXY_URL || 'http://localhost:3000'
baseURL: process.env.REQUESTY_PROXY_URL || `http://localhost:${process.env.PROXY_PORT || '3000'}`
};

case 'openrouter':
return {
model: envModel || 'deepseek/deepseek-chat',
apiKey: process.env.OPENROUTER_API_KEY || '',
baseURL: process.env.OPENROUTER_PROXY_URL || 'http://localhost:3000'
baseURL: process.env.OPENROUTER_PROXY_URL || `http://localhost:${process.env.PROXY_PORT || '3000'}`
};

case 'onnx':
return {
model: 'onnx-local',
apiKey: 'local',
baseURL: process.env.ONNX_PROXY_URL || 'http://localhost:3001'
baseURL: process.env.ONNX_PROXY_URL || `http://localhost:${process.env.ONNX_PROXY_PORT || '3001'}`
};

case 'anthropic':
Expand Down
83 changes: 80 additions & 3 deletions agentic-flow/src/cli-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const VERSION = packageJson.version;

class AgenticFlowCLI {
private proxyServer: any = null;
private proxyPort: number = 3000;
private proxyPort: number = parseInt(process.env.PROXY_PORT || '3000', 10);

async start() {
const options = parseArgs();
Expand Down Expand Up @@ -280,6 +280,7 @@ class AgenticFlowCLI {

// Determine which provider to use
const useONNX = this.shouldUseONNX(options);
const useOllama = this.shouldUseOllama(options);
const useOpenRouter = this.shouldUseOpenRouter(options);
const useGemini = this.shouldUseGemini(options);
// Requesty temporarily disabled - keep proxy files for future use
Expand Down Expand Up @@ -308,6 +309,9 @@ class AgenticFlowCLI {
} else if (useRequesty) {
console.log('🚀 Initializing Requesty proxy...');
await this.startRequestyProxy(options.model);
} else if (useOllama) {
console.log('🚀 Initializing Ollama local proxy...');
await this.startOllamaProxy(options.model);
} else if (useOpenRouter) {
console.log('🚀 Initializing OpenRouter proxy...');
await this.startOpenRouterProxy(options.model);
Expand Down Expand Up @@ -349,6 +353,22 @@ class AgenticFlowCLI {
return false;
}

private shouldUseOllama(options: any): boolean {
// Use Ollama if:
// 1. Provider is explicitly set to ollama
// 2. PROVIDER env var is set to ollama
// 3. USE_OLLAMA env var is set
if (options.provider === 'ollama' || process.env.PROVIDER === 'ollama') {
return true;
}

if (process.env.USE_OLLAMA === 'true') {
return true;
}

return false;
}

private shouldUseGemini(options: any): boolean {
// Use Gemini if:
// 1. Provider is explicitly set to gemini
Expand Down Expand Up @@ -395,6 +415,13 @@ class AgenticFlowCLI {
}

private shouldUseOpenRouter(options: any): boolean {
// Don't use OpenRouter if Ollama is explicitly requested — otherwise a stray
// OPENROUTER_API_KEY in the environment captures the run via the key-based
// auto-selection below.
if (options.provider === 'ollama' || process.env.PROVIDER === 'ollama' || process.env.USE_OLLAMA === 'true') {
return false;
}

// Don't use OpenRouter if ONNX, Gemini, or Requesty is explicitly requested
if (options.provider === 'onnx' || process.env.USE_ONNX === 'true' || process.env.PROVIDER === 'onnx') {
return false;
Expand Down Expand Up @@ -432,6 +459,48 @@ class AgenticFlowCLI {
return false;
}

private async startOllamaProxy(modelOverride?: string): Promise<void> {
// Ollama exposes an OpenAI-compatible /v1/chat/completions, which is exactly
// what AnthropicToOpenRouterProxy speaks — so this needs no new proxy class.
// The key below is a placeholder, not a credential: Ollama ignores
// Authorization entirely, and requiring a real one would defeat the point of
// a local provider.
const host = (process.env.OLLAMA_HOST || 'http://localhost:11434').replace(/\/+$/, '');

logger.info('Starting integrated Ollama proxy', { host });

const defaultModel = modelOverride ||
process.env.OLLAMA_MODEL ||
process.env.COMPLETION_MODEL ||
'qwen2.5-coder:7b';

const capabilities = detectModelCapabilities(defaultModel);

const proxy = new AnthropicToOpenRouterProxy({
openrouterApiKey: 'ollama-local-placeholder',
openrouterBaseUrl: `${host}/v1`,
defaultModel,
capabilities: capabilities
});

proxy.start(this.proxyPort);
this.proxyServer = proxy;

process.env.ANTHROPIC_BASE_URL = `http://localhost:${this.proxyPort}`;

if (!process.env.ANTHROPIC_API_KEY) {
process.env.ANTHROPIC_API_KEY = 'sk-ant-proxy-dummy-key';
}

console.log(`🔗 Proxy Mode: Ollama (local, no API key)`);
console.log(`🔧 Proxy URL: http://localhost:${this.proxyPort}`);
console.log(`🔧 Ollama: ${host}/v1`);
console.log(`🤖 Model: ${defaultModel}\n`);

// Wait for proxy to be ready
await new Promise(resolve => setTimeout(resolve, 1500));
}

private async startOpenRouterProxy(modelOverride?: string): Promise<void> {
const openrouterKey = process.env.OPENROUTER_API_KEY;

Expand Down Expand Up @@ -709,6 +778,7 @@ Get your key at: https://openrouter.ai/keys
const { AnthropicToOpenRouterProxy } = await import('./proxy/anthropic-to-openrouter.js');
const proxy = new AnthropicToOpenRouterProxy({
openrouterApiKey: apiKey,
openrouterBaseUrl: process.env.ANTHROPIC_PROXY_BASE_URL,
defaultModel: finalModel
});

Expand Down Expand Up @@ -945,13 +1015,16 @@ PERFORMANCE:
// Check for API key (unless using ONNX)
const isOnnx = options.provider === 'onnx' || process.env.USE_ONNX === 'true' || process.env.PROVIDER === 'onnx';

if (!isOnnx && !useOpenRouter && !useGemini && !useRequesty && !process.env.ANTHROPIC_API_KEY) {
const isOllama = options.provider === 'ollama' || process.env.USE_OLLAMA === 'true' || process.env.PROVIDER === 'ollama';

if (!isOnnx && !isOllama && !useOpenRouter && !useGemini && !useRequesty && !process.env.ANTHROPIC_API_KEY) {
console.error('\n❌ Error: ANTHROPIC_API_KEY is required\n');
console.error('Please set your API key:');
console.error(' export ANTHROPIC_API_KEY=sk-ant-xxxxx\n');
console.error('Or use alternative providers:');
console.error(' --provider openrouter (requires OPENROUTER_API_KEY)');
console.error(' --provider gemini (requires GOOGLE_GEMINI_API_KEY)');
console.error(' --provider ollama (free local inference via Ollama, no key)');
console.error(' --provider onnx (free local inference)\n');
process.exit(1);
}
Expand Down Expand Up @@ -1014,6 +1087,10 @@ PERFORMANCE:
const model = options.model || 'gemini-2.0-flash-exp';
console.log(`🔧 Provider: Google Gemini`);
console.log(`🔧 Model: ${model}\n`);
} else if (isOllama) {
const model = options.model || process.env.OLLAMA_MODEL || process.env.COMPLETION_MODEL || 'qwen2.5-coder:7b';
console.log(`🔧 Provider: Ollama (local, no API key)`);
console.log(`🔧 Model: ${model}\n`);
} else if (options.provider === 'onnx' || process.env.USE_ONNX === 'true' || process.env.PROVIDER === 'onnx') {
console.log(`🔧 Provider: ONNX Local (Phi-4-mini)`);
console.log(`💾 Free local inference - no API costs`);
Expand Down Expand Up @@ -1216,7 +1293,7 @@ WORKERS COMMANDS:
OPTIONS:
--task, -t <task> Task description for agent mode
--model, -m <model> Model to use (triggers OpenRouter if contains "/")
--provider, -p <name> Provider to use (anthropic, openrouter, gemini, onnx)
--provider, -p <name> Provider to use (anthropic, openrouter, gemini, onnx, ollama)
--stream, -s Enable real-time streaming output
--help, -h Show this help message

Expand Down
4 changes: 2 additions & 2 deletions agentic-flow/src/router/providers/onnx-local-optimized.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ try {
}

import { get_encoding } from 'tiktoken';
import { ensurePhi4Model, ModelDownloader } from '../../utils/model-downloader.js';
import { ensurePhi4Model, ModelDownloader, PHI4_MODEL_PATH } from '../../utils/model-downloader.js';
import type {
ChatParams,
ChatResponse,
Expand Down Expand Up @@ -50,7 +50,7 @@ export class OptimizedONNXProvider extends ONNXLocalProvider {
super(config);

this.optimizedConfig = {
modelPath: config.modelPath || './models/phi-4-mini/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx',
modelPath: config.modelPath || PHI4_MODEL_PATH,
executionProviders: config.executionProviders || ['cpu'],
maxTokens: config.maxTokens || 200,
temperature: config.temperature || 0.3, // Lower for code (more deterministic)
Expand Down
4 changes: 2 additions & 2 deletions agentic-flow/src/router/providers/onnx-local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ try {
import * as fs from 'fs';
import * as path from 'path';
import { get_encoding } from 'tiktoken';
import { ensurePhi4Model, ModelDownloader } from '../../utils/model-downloader.js';
import { ensurePhi4Model, ModelDownloader, PHI4_MODEL_PATH } from '../../utils/model-downloader.js';
import type {
LLMProvider,
ChatParams,
Expand Down Expand Up @@ -51,7 +51,7 @@ export class ONNXLocalProvider implements LLMProvider {

constructor(config: ONNXLocalConfig = {}) {
this.config = {
modelPath: config.modelPath || './models/phi-4/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx',
modelPath: config.modelPath || PHI4_MODEL_PATH,
executionProviders: config.executionProviders || ['cpu'],
maxTokens: config.maxTokens || 100,
temperature: config.temperature || 0.7
Expand Down
3 changes: 2 additions & 1 deletion agentic-flow/src/router/providers/onnx-phi4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { HfInference } from '@huggingface/inference';
import { PHI4_MODEL_PATH } from '../../utils/model-downloader.js';
import type {
LLMProvider,
ChatParams,
Expand Down Expand Up @@ -33,7 +34,7 @@ export class ONNXPhi4Provider implements LLMProvider {

private config: Required<ONNXPhi4Config>;
private hf: HfInference;
private modelPath = './models/phi-4/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx';
private modelPath = PHI4_MODEL_PATH;

constructor(config: ONNXPhi4Config = {}) {
this.config = {
Expand Down
3 changes: 2 additions & 1 deletion agentic-flow/src/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { AnthropicProvider } from './providers/anthropic.js';
import { ONNXLocalProvider } from './providers/onnx-local.js';
import { GeminiProvider } from './providers/gemini.js';
import { OllamaProvider } from './providers/ollama.js';
import { PHI4_MODEL_PATH } from '../utils/model-downloader.js';

export class ModelRouter {
private config: RouterConfig;
Expand Down Expand Up @@ -166,7 +167,7 @@ export class ModelRouter {
const provider = new ONNXLocalProvider({
modelPath:
this.config.providers.onnx.modelPath ||
'./models/phi-4/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx',
PHI4_MODEL_PATH,
executionProviders: this.config.providers.onnx.executionProviders || ['cpu'],
maxTokens: this.config.providers.onnx.maxTokens || 100,
temperature: this.config.providers.onnx.temperature || 0.7,
Expand Down
3 changes: 2 additions & 1 deletion agentic-flow/src/router/test-onnx-benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { ONNXLocalProvider } from './providers/onnx-local.js';
import { PHI4_MODEL_PATH } from '../utils/model-downloader.js';

interface BenchmarkResult {
test: string;
Expand All @@ -19,7 +20,7 @@ async function runBenchmark() {
console.log('================================================\n');

const provider = new ONNXLocalProvider({
modelPath: './models/phi-4/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx',
modelPath: PHI4_MODEL_PATH,
executionProviders: ['cpu'],
maxTokens: 50,
temperature: 0.7
Expand Down
3 changes: 2 additions & 1 deletion agentic-flow/src/router/test-onnx-local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
*/

import { ONNXLocalProvider } from './providers/onnx-local.js';
import { PHI4_MODEL_PATH } from '../utils/model-downloader.js';

async function testONNXLocal() {
console.log('🧪 Testing ONNX Local Inference (Phi-4 CPU)\n');

try {
const provider = new ONNXLocalProvider({
modelPath: './models/phi-4/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx',
modelPath: PHI4_MODEL_PATH,
executionProviders: ['cpu'],
maxTokens: 50
});
Expand Down
25 changes: 23 additions & 2 deletions agentic-flow/src/utils/model-downloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,27 @@ import { dirname, join } from 'path';
import { pipeline } from 'stream/promises';
import { createHash } from 'crypto';
import { readFileSync } from 'fs';
import { homedir } from 'os';

/**
* Where ONNX models live on disk.
*
* These paths were previously relative ('./models/...'), so they resolved
* against process.cwd() and a ~4.6GB download landed in whatever directory the
* command happened to run from — re-downloaded once per working directory, and
* left untracked inside any repository it was invoked in.
*
* Exported so the reader (router/providers/onnx-local.ts) can resolve the same
* path the writer uses; the two previously disagreed (phi-4 vs phi-4-mini).
*/
export const MODEL_ROOT = process.env.AGENTIC_FLOW_MODEL_DIR
|| join(homedir(), '.agentic-flow', 'models');

export const PHI4_MODEL_PATH = join(
MODEL_ROOT, 'phi-4-mini', 'cpu_and_mobile', 'cpu-int4-rtn-block-32-acc-level-4', 'model.onnx'
);

export const PHI4_MODEL_DATA_PATH = `${PHI4_MODEL_PATH}.data`;

export interface DownloadProgress {
downloaded: number;
Expand All @@ -34,13 +55,13 @@ export class ModelDownloader {
private phi4Model: ModelInfo = {
repo: 'microsoft/Phi-4-mini-instruct-onnx',
filename: 'cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx',
localPath: './models/phi-4-mini/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx'
localPath: PHI4_MODEL_PATH
};

private phi4ModelData: ModelInfo = {
repo: 'microsoft/Phi-4-mini-instruct-onnx',
filename: 'cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx.data',
localPath: './models/phi-4-mini/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx.data'
localPath: PHI4_MODEL_DATA_PATH
};

/**
Expand Down