titan-agent 5.3.2 → 5.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/agent.js +11 -1
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/agentLoop.js +36 -1
- package/dist/agent/agentLoop.js.map +1 -1
- package/dist/agent/session.js +106 -5
- package/dist/agent/session.js.map +1 -1
- package/dist/agent/subAgent.js +62 -1
- package/dist/agent/subAgent.js.map +1 -1
- package/dist/config/config.js +30 -8
- package/dist/config/config.js.map +1 -1
- package/dist/config/schema.js +25 -2
- package/dist/config/schema.js.map +1 -1
- package/dist/gateway/server.js +32 -1
- package/dist/gateway/server.js.map +1 -1
- package/dist/memory/graph.js +49 -15
- package/dist/memory/graph.js.map +1 -1
- package/dist/memory/index.js +192 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/memory.js +1 -0
- package/dist/memory/memory.js.map +1 -1
- package/dist/mesh/transport.js +60 -8
- package/dist/mesh/transport.js.map +1 -1
- package/dist/providers/anthropic.js +3 -2
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/base.js.map +1 -1
- package/dist/providers/google.js +94 -20
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/modelCapabilities.js +59 -0
- package/dist/providers/modelCapabilities.js.map +1 -0
- package/dist/providers/ollama.js +3 -2
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.js +4 -3
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openai_compat.js +3 -2
- package/dist/providers/openai_compat.js.map +1 -1
- package/dist/providers/router.js +63 -21
- package/dist/providers/router.js.map +1 -1
- package/dist/safety/fabricationGuard.js +140 -0
- package/dist/safety/fabricationGuard.js.map +1 -0
- package/dist/skills/builtin/gepa.js +23 -1
- package/dist/skills/builtin/gepa.js.map +1 -1
- package/dist/skills/builtin/model_trainer.js +31 -4
- package/dist/skills/builtin/model_trainer.js.map +1 -1
- package/dist/skills/builtin/self_improve.js +50 -2
- package/dist/skills/builtin/self_improve.js.map +1 -1
- package/dist/utils/constants.js +2 -2
- package/dist/utils/constants.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/skills/builtin/model_trainer.ts"],"sourcesContent":["/**\n * TITAN — Model Trainer Skill (Built-in)\n * Fine-tunes local Ollama models on TITAN's own conversation history\n * using the GPU (RTX 5090) on Titan PC.\n *\n * Pipeline: prepare training data → launch LoRA fine-tune → poll progress → deploy to Ollama\n */\nimport { registerSkill } from '../registry.js';\nimport { loadConfig } from '../../config/config.js';\nimport logger from '../../utils/logger.js';\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, appendFileSync } from 'fs';\nimport { join } from 'path';\nimport { TITAN_HOME } from '../../utils/constants.js';\nimport { execSync } from 'child_process';\nimport { EventEmitter } from 'events';\n\nconst COMPONENT = 'ModelTrainer';\n\n// ── Training Progress Events ─────────────────────────────────────────\nexport interface TrainingProgressEvent {\n type: 'info' | 'progress' | 'success' | 'error' | 'complete';\n phase: 'generate' | 'train' | 'deploy' | 'prepare';\n message: string;\n timestamp: string;\n detail?: {\n category?: string;\n current?: number;\n total?: number;\n pct?: number;\n model?: string;\n loss?: number;\n examples?: number;\n };\n}\n\nexport const trainingEvents = new EventEmitter();\ntrainingEvents.setMaxListeners(50);\n\nfunction emitProgress(event: Omit<TrainingProgressEvent, 'timestamp'>): void {\n const full: TrainingProgressEvent = { ...event, timestamp: new Date().toISOString() };\n trainingEvents.emit('progress', full);\n // Also persist to a rolling log file for the UI to poll as fallback\n try {\n const logPath = join(TITAN_HOME, 'training-progress.jsonl');\n appendFileSync(logPath, JSON.stringify(full) + '\\n', 'utf-8');\n } catch { /* best-effort */ }\n}\n\n// ── Paths ────────────────────────────────────────────────────────────\nconst TRAINING_DIR = join(TITAN_HOME, 'training-data');\nconst TRAINING_RUNS_DIR = join(TITAN_HOME, 'training-runs');\nconst TRAINING_HISTORY_PATH = join(TITAN_HOME, 'training-history.jsonl');\n\n// ── Types ────────────────────────────────────────────────────────────\n\ninterface TrainingRun {\n id: string;\n status: 'preparing' | 'training' | 'completed' | 'failed' | 'deploying';\n startedAt: string;\n completedAt?: string;\n baseModel: string;\n method: string;\n dataPoints: number;\n epochs?: number;\n finalLoss?: number;\n outputModel?: string;\n error?: string;\n}\n\ninterface TrainingDataPoint {\n instruction: string;\n response: string;\n toolsUsed?: string[];\n score?: number;\n}\n\n// ── Active training tracking ─────────────────────────────────────────\nconst activeRuns: Map<string, TrainingRun> = new Map();\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nfunction ensureDirs(): void {\n for (const dir of [TRAINING_DIR, TRAINING_RUNS_DIR]) {\n mkdirSync(dir, { recursive: true });\n }\n}\n\nfunction getRunId(): string {\n return `train-${Date.now().toString(36)}`;\n}\n\nfunction _appendTrainingHistory(run: TrainingRun): void {\n ensureDirs();\n appendFileSync(TRAINING_HISTORY_PATH, JSON.stringify(run) + '\\n', 'utf-8');\n}\n\nfunction _readTrainingHistory(limit: number = 20): TrainingRun[] {\n if (!existsSync(TRAINING_HISTORY_PATH)) return [];\n try {\n const lines = readFileSync(TRAINING_HISTORY_PATH, 'utf-8').split('\\n').filter(l => l.trim());\n const runs = lines.map(l => {\n try { return JSON.parse(l) as TrainingRun; }\n catch { return null; }\n }).filter(Boolean) as TrainingRun[];\n return runs.slice(-limit);\n } catch {\n return [];\n }\n}\nvoid _appendTrainingHistory;\nvoid _readTrainingHistory;\n\n/** Extract high-quality training pairs from TITAN's session database */\nfunction extractTrainingData(): TrainingDataPoint[] {\n const dataPoints: TrainingDataPoint[] = [];\n\n // Try to read from session history files\n const sessionsDir = join(TITAN_HOME, 'sessions');\n if (!existsSync(sessionsDir)) {\n logger.warn(COMPONENT, 'No sessions directory found');\n return dataPoints;\n }\n\n try {\n const files = readdirSync(sessionsDir).filter(f => f.endsWith('.json'));\n for (const file of files.slice(-100)) { // Last 100 sessions\n try {\n const sessionData = JSON.parse(readFileSync(join(sessionsDir, file), 'utf-8'));\n const messages = sessionData.messages || sessionData.history || [];\n\n for (let i = 0; i < messages.length - 1; i++) {\n const msg = messages[i];\n const nextMsg = messages[i + 1];\n\n if (msg.role === 'user' && nextMsg.role === 'assistant' && nextMsg.content) {\n // Only include substantive exchanges\n if (msg.content.length > 10 && nextMsg.content.length > 20) {\n dataPoints.push({\n instruction: msg.content,\n response: nextMsg.content,\n toolsUsed: nextMsg.toolsUsed || [],\n });\n }\n }\n }\n } catch {\n // Skip corrupt session files\n }\n }\n } catch (e) {\n logger.warn(COMPONENT, `Error reading sessions: ${(e as Error).message}`);\n }\n\n // Also read from learning data for quality signals\n const learningPath = join(TITAN_HOME, 'learning.json');\n if (existsSync(learningPath)) {\n try {\n const learning = JSON.parse(readFileSync(learningPath, 'utf-8'));\n // Use tool success rates to score training data quality\n if (learning.toolSuccessRates) {\n // Boost data points that used successful tools\n for (const dp of dataPoints) {\n if (dp.toolsUsed && dp.toolsUsed.length > 0) {\n const avgRate = dp.toolsUsed.reduce((sum: number, tool: string) => {\n const rate = learning.toolSuccessRates[tool];\n return sum + (rate?.successRate || 0.5);\n }, 0) / dp.toolsUsed.length;\n dp.score = avgRate;\n }\n }\n }\n } catch {\n // Ignore learning data errors\n }\n }\n\n // Sort by quality score (higher is better) and filter\n return dataPoints\n .sort((a, b) => (b.score || 0.5) - (a.score || 0.5))\n .filter(dp => (dp.score || 0.5) >= 0.4); // Exclude low-quality pairs\n}\n\n// ── Tool implementations ─────────────────────────────────────────────\n\nasync function trainPrepare(args: Record<string, unknown>): Promise<string> {\n const minSamples = (args.minSamples as number) || 50;\n\n ensureDirs();\n\n logger.info(COMPONENT, 'Extracting training data from session history...');\n const dataPoints = extractTrainingData();\n\n if (dataPoints.length < minSamples) {\n return `Not enough high-quality training data. Found ${dataPoints.length} pairs, need at least ${minSamples}. Keep using TITAN to build up more conversation history.`;\n }\n\n // Format as JSONL for training\n const jsonlPath = join(TRAINING_DIR, 'train.jsonl');\n const jsonlContent = dataPoints.map(dp => JSON.stringify({\n messages: [\n { role: 'user', content: dp.instruction },\n { role: 'assistant', content: dp.response },\n ],\n })).join('\\n');\n\n writeFileSync(jsonlPath, jsonlContent, 'utf-8');\n\n // Create a validation split (10%)\n const valSize = Math.max(1, Math.floor(dataPoints.length * 0.1));\n const valData = dataPoints.slice(0, valSize);\n const valPath = join(TRAINING_DIR, 'val.jsonl');\n writeFileSync(valPath, valData.map(dp => JSON.stringify({\n messages: [\n { role: 'user', content: dp.instruction },\n { role: 'assistant', content: dp.response },\n ],\n })).join('\\n'), 'utf-8');\n\n return [\n `## Training Data Prepared`,\n ``,\n `| Stat | Value |`,\n `|------|-------|`,\n `| Total pairs | ${dataPoints.length} |`,\n `| Training set | ${dataPoints.length - valSize} |`,\n `| Validation set | ${valSize} |`,\n `| Avg quality score | ${(dataPoints.reduce((s, d) => s + (d.score || 0.5), 0) / dataPoints.length).toFixed(2)} |`,\n ``,\n `Training data: \\`${jsonlPath}\\``,\n `Validation data: \\`${valPath}\\``,\n ``,\n `Ready to train. Use \\`train_start\\` to begin fine-tuning.`,\n ].join('\\n');\n}\n\nasync function trainStart(args: Record<string, unknown>): Promise<string> {\n const config = loadConfig();\n const trainingConfig = (config as Record<string, unknown>).training as Record<string, unknown> | undefined;\n\n if (trainingConfig && trainingConfig.enabled === false) {\n return 'Training is disabled in config. Set training.enabled = true to enable.';\n }\n\n // Model resolution: explicit arg → config → active model (if local/ollama) → fallback\n const activeModel = config.agent?.model || '';\n const activeModelName = activeModel.replace(/^ollama\\//, '');\n const isLocalModel = activeModel.startsWith('ollama/') || (!activeModel.includes('/') && activeModel.length > 0);\n const baseModel = (args.baseModel as string)\n || (trainingConfig?.baseModel as string)\n || (isLocalModel ? activeModelName : '')\n || 'qwen3.5:35b';\n const method = (args.method as string) || (trainingConfig?.method as string) || 'lora';\n const budgetMinutes = (args.budgetMinutes as number) || (trainingConfig?.budgetMinutes as number) || 30;\n const epochs = (args.epochs as number) || 3;\n\n const trainDataPath = join(TRAINING_DIR, 'train.jsonl');\n if (!existsSync(trainDataPath)) {\n return 'No training data found. Run `train_prepare` first.';\n }\n\n const dataLines = readFileSync(trainDataPath, 'utf-8').split('\\n').filter(l => l.trim());\n if (dataLines.length < 10) {\n return `Only ${dataLines.length} training samples — need at least 10. Run \\`train_prepare\\` to extract more data.`;\n }\n\n const runId = getRunId();\n const runDir = join(TRAINING_RUNS_DIR, runId);\n mkdirSync(runDir, { recursive: true });\n\n const run: TrainingRun = {\n id: runId,\n status: 'training',\n startedAt: new Date().toISOString(),\n baseModel,\n method,\n dataPoints: dataLines.length,\n epochs,\n };\n activeRuns.set(runId, run);\n\n // Generate training script\n const trainScript = `#!/usr/bin/env python3\n\"\"\"TITAN Auto-Training Script — LoRA fine-tuning via unsloth\"\"\"\nimport os, sys, json, time, torch\n\n# Check if unsloth is available\ntry:\n from unsloth import FastLanguageModel\n from trl import SFTTrainer\n from transformers import TrainingArguments\n from datasets import load_dataset\n HAS_UNSLOTH = True\nexcept ImportError:\n HAS_UNSLOTH = False\n print(\"WARNING: unsloth not installed. Install with: pip install unsloth\")\n print(\"Falling back to simulation mode for testing.\")\n\nOUTPUT_DIR = \"${runDir}\"\nDATA_PATH = \"${trainDataPath}\"\nBASE_MODEL = \"${baseModel}\"\nEPOCHS = ${epochs}\nMAX_MINUTES = ${budgetMinutes}\n\n# Map Ollama model names to HuggingFace model IDs\nOLLAMA_TO_HF = {\n \"qwen3.5:35b\": \"Qwen/Qwen3.5-9B\", # 35B MoE too large for single GPU — use 9B dense\n \"qwen3.5:7b\": \"Qwen/Qwen3.5-7B\",\n \"qwen3:30b\": \"Qwen/Qwen3-30B-A3B\",\n \"qwen3:8b\": \"Qwen/Qwen3-8B\",\n \"llama3.1:8b\": \"meta-llama/Llama-3.1-8B-Instruct\",\n \"llama3.1:70b\": \"meta-llama/Llama-3.1-70B-Instruct\",\n \"mistral:7b\": \"mistralai/Mistral-7B-Instruct-v0.3\",\n \"gemma2:9b\": \"google/gemma-2-9b-it\",\n \"phi3:14b\": \"microsoft/Phi-3-medium-128k-instruct\",\n \"devstral-small-2\": \"mistralai/Devstral-Small-2505\",\n}\n\ndef resolve_model_name(name):\n \"\"\"Resolve Ollama model name to HuggingFace model ID.\"\"\"\n if name in OLLAMA_TO_HF:\n hf_name = OLLAMA_TO_HF[name]\n print(f\"Resolved Ollama model '{name}' -> HuggingFace '{hf_name}'\")\n return hf_name\n # If it looks like a HF model (contains /), use as-is\n if \"/\" in name:\n return name\n print(f\"WARNING: Unknown model '{name}', trying as HuggingFace ID directly\")\n return name\n\ndef main():\n start = time.time()\n\n if not HAS_UNSLOTH:\n # Simulation mode — useful for testing the pipeline\n print(f\"[SIM] Loading base model: {BASE_MODEL}\")\n print(f\"[SIM] Training data: {DATA_PATH}\")\n time.sleep(2)\n for epoch in range(1, EPOCHS + 1):\n elapsed = time.time() - start\n if elapsed > MAX_MINUTES * 60:\n print(f\"[SIM] Time budget exhausted at epoch {epoch}\")\n break\n loss = 2.5 - (epoch * 0.3)\n print(f\"Epoch {epoch}/{EPOCHS} — loss: {loss:.4f}\")\n time.sleep(1)\n\n # Write results\n with open(os.path.join(OUTPUT_DIR, \"results.json\"), \"w\") as f:\n json.dump({\"status\": \"simulated\", \"epochs\": EPOCHS, \"final_loss\": 0.8, \"model_path\": None}, f)\n print(\"Training complete (simulated)\")\n return\n\n # Real training with unsloth\n hf_model = resolve_model_name(BASE_MODEL)\n print(f\"Loading base model: {hf_model}\")\n model, tokenizer = FastLanguageModel.from_pretrained(\n model_name=hf_model,\n max_seq_length=2048,\n dtype=None, # Auto-detect\n load_in_4bit=True,\n )\n\n model = FastLanguageModel.get_peft_model(\n model,\n r=16,\n target_modules=[\"q_proj\", \"k_proj\", \"v_proj\", \"o_proj\", \"gate_proj\", \"up_proj\", \"down_proj\"],\n lora_alpha=16,\n lora_dropout=0,\n bias=\"none\",\n use_gradient_checkpointing=\"unsloth\",\n )\n\n dataset = load_dataset(\"json\", data_files=DATA_PATH, split=\"train\")\n print(f\"Loaded {len(dataset)} training examples\")\n\n # Format chat messages into text using the tokenizer's chat template\n def format_chat(example):\n text = tokenizer.apply_chat_template(example[\"messages\"], tokenize=False, add_generation_prompt=False)\n return {\"text\": text}\n\n dataset = dataset.map(format_chat)\n print(f\"Formatted dataset — sample length: {len(dataset[0]['text'])} chars\")\n\n trainer = SFTTrainer(\n model=model,\n tokenizer=tokenizer,\n train_dataset=dataset,\n dataset_text_field=\"text\",\n max_seq_length=2048,\n packing=True,\n args=TrainingArguments(\n output_dir=OUTPUT_DIR,\n per_device_train_batch_size=${(args.batchSize as number) || 4},\n gradient_accumulation_steps=4,\n num_train_epochs=EPOCHS,\n learning_rate=${(args.learningRate as number) || 2e-4},\n fp16=False,\n bf16=True,\n logging_steps=1,\n save_strategy=\"epoch\",\n warmup_steps=10,\n weight_decay=0.01,\n lr_scheduler_type=\"cosine\",\n max_steps=-1,\n report_to=\"none\",\n ),\n )\n\n print(\"Starting training...\")\n gpu_stats = torch.cuda.get_device_properties(0)\n print(f\"GPU: {gpu_stats.name}, VRAM: {gpu_stats.total_memory / 1024**3:.1f} GB\")\n result = trainer.train()\n\n # Save LoRA adapter\n adapter_dir = os.path.join(OUTPUT_DIR, \"lora_adapter\")\n model.save_pretrained(adapter_dir)\n tokenizer.save_pretrained(adapter_dir)\n print(f\"LoRA adapter saved to {adapter_dir}\")\n\n # Also save as merged GGUF for Ollama\n print(\"Saving merged model as GGUF (Q4_K_M)...\")\n try:\n model.save_pretrained_gguf(\n os.path.join(OUTPUT_DIR, \"gguf\"),\n tokenizer,\n quantization_method=\"q4_k_m\",\n )\n print(\"GGUF export complete\")\n except Exception as e:\n print(f\"GGUF export failed (non-fatal): {e}\")\n\n with open(os.path.join(OUTPUT_DIR, \"results.json\"), \"w\") as f:\n json.dump({\n \"status\": \"completed\",\n \"epochs\": EPOCHS,\n \"final_loss\": result.training_loss,\n \"model_path\": adapter_dir,\n \"gguf_path\": os.path.join(OUTPUT_DIR, \"gguf\"),\n }, f)\n\n print(f\"Training complete — loss: {result.training_loss:.4f}\")\n\nif __name__ == \"__main__\":\n main()\n`;\n\n const scriptPath = join(runDir, 'train.py');\n writeFileSync(scriptPath, trainScript, 'utf-8');\n\n // Launch training as background process\n try {\n // Prefer venv python (has unsloth installed), fall back to system python3\n const venvCandidates = [\n join(TITAN_HOME, 'venv', 'bin', 'python'), // ~/.titan/venv\n '/opt/TITAN/venv/bin/python', // production deploy\n ];\n const pythonBin = venvCandidates.find(p => existsSync(p)) ?? 'python3';\n execSync(`${pythonBin} \"${scriptPath}\" > \"${join(runDir, 'train.log')}\" 2>&1 &`, {\n stdio: 'pipe',\n timeout: 5000,\n });\n } catch {\n // Background process launch — may appear to \"fail\" but actually started\n logger.info(COMPONENT, 'Training process launched in background');\n }\n\n // Save run metadata\n writeFileSync(join(runDir, 'meta.json'), JSON.stringify(run, null, 2), 'utf-8');\n\n return [\n `## Training Started`,\n ``,\n `| Setting | Value |`,\n `|---------|-------|`,\n `| Run ID | ${runId} |`,\n `| Base model | ${baseModel} |`,\n `| Method | ${method} |`,\n `| Training samples | ${dataLines.length} |`,\n `| Epochs | ${epochs} |`,\n `| Budget | ${budgetMinutes} min |`,\n ``,\n `Training log: \\`${join(runDir, 'train.log')}\\``,\n `Use \\`train_status\\` to check progress.`,\n ].join('\\n');\n}\n\nasync function trainStatus(args: Record<string, unknown>): Promise<string> {\n const runId = args.runId as string | undefined;\n\n if (runId) {\n const runDir = join(TRAINING_RUNS_DIR, runId);\n if (!existsSync(runDir)) {\n return `Training run \"${runId}\" not found.`;\n }\n\n const logPath = join(runDir, 'train.log');\n const resultsPath = join(runDir, 'results.json');\n\n const lines: string[] = [`## Training Run: ${runId}\\n`];\n\n if (existsSync(resultsPath)) {\n const results = JSON.parse(readFileSync(resultsPath, 'utf-8'));\n lines.push(`**Status**: ${results.status}`);\n lines.push(`**Final loss**: ${results.final_loss}`);\n lines.push(`**Epochs**: ${results.epochs}`);\n if (results.model_path) {\n lines.push(`**Model path**: ${results.model_path}`);\n }\n } else if (existsSync(logPath)) {\n const log = readFileSync(logPath, 'utf-8');\n const lastLines = log.trim().split('\\n').slice(-10);\n lines.push('**Status**: training...\\n');\n lines.push('### Recent log output:');\n lines.push('```');\n lines.push(lastLines.join('\\n'));\n lines.push('```');\n } else {\n lines.push('**Status**: waiting to start...');\n }\n\n return lines.join('\\n');\n }\n\n // List all runs\n const lines: string[] = ['## Training Runs\\n'];\n\n if (existsSync(TRAINING_RUNS_DIR)) {\n const dirs = readdirSync(TRAINING_RUNS_DIR, { withFileTypes: true })\n .filter(d => d.isDirectory())\n .map(d => d.name)\n .sort()\n .reverse();\n\n if (dirs.length > 0) {\n lines.push('| Run ID | Base Model | Status | Loss | Samples |');\n lines.push('|--------|-----------|--------|------|---------|');\n\n for (const dir of dirs.slice(0, 10)) {\n const metaPath = join(TRAINING_RUNS_DIR, dir, 'meta.json');\n const resultsPath = join(TRAINING_RUNS_DIR, dir, 'results.json');\n\n if (existsSync(metaPath)) {\n const meta = JSON.parse(readFileSync(metaPath, 'utf-8'));\n let status = 'unknown';\n let loss = '-';\n\n if (existsSync(resultsPath)) {\n const results = JSON.parse(readFileSync(resultsPath, 'utf-8'));\n status = results.status || 'completed';\n loss = results.final_loss?.toFixed(4) || '-';\n } else {\n status = 'training...';\n }\n\n lines.push(`| ${dir} | ${meta.baseModel} | ${status} | ${loss} | ${meta.dataPoints} |`);\n }\n }\n } else {\n lines.push('No training runs found. Use `train_prepare` then `train_start` to begin.');\n }\n } else {\n lines.push('No training runs found.');\n }\n\n return lines.join('\\n');\n}\n\nasync function trainDeploy(args: Record<string, unknown>): Promise<string> {\n const runId = args.runId as string;\n const modelName = (args.modelName as string) || 'titan-custom';\n\n if (!runId) {\n return 'Error: runId is required. Use `train_status` to find completed runs.';\n }\n\n const runDir = join(TRAINING_RUNS_DIR, runId);\n const resultsPath = join(runDir, 'results.json');\n\n if (!existsSync(resultsPath)) {\n return `Training run \"${runId}\" not found or not completed.`;\n }\n\n const results = JSON.parse(readFileSync(resultsPath, 'utf-8'));\n if (results.status === 'simulated') {\n return [\n `## Deploy — Simulated Run`,\n ``,\n `This was a simulated training run (unsloth not installed).`,\n `To deploy a real model:`,\n `1. Install unsloth on Titan PC: \\`pip install unsloth\\``,\n `2. Run \\`train_start\\` again for real training`,\n `3. Then \\`train_deploy\\` to create an Ollama model`,\n ].join('\\n');\n }\n\n if (!results.model_path || !existsSync(results.model_path)) {\n return `Model adapter not found at ${results.model_path}. Training may not have completed successfully.`;\n }\n\n // Create Modelfile for Ollama\n const config = loadConfig();\n const trainingConfig = (config as Record<string, unknown>).training as Record<string, unknown> | undefined;\n // Use configured base model, or active local model, or fallback\n const activeModel = config.agent?.model || '';\n const activeModelName = activeModel.replace(/^ollama\\//, '');\n const isLocalModel = activeModel.startsWith('ollama/') || (!activeModel.includes('/') && activeModel.length > 0);\n const baseModel = (trainingConfig?.baseModel as string)\n || (isLocalModel ? activeModelName : '')\n || 'qwen3.5:35b';\n\n const modelfilePath = join(runDir, 'Modelfile');\n const modelfileContent = `FROM ${baseModel}\nADAPTER ${results.model_path}\n\nPARAMETER temperature 0.7\nPARAMETER num_ctx 65536\n\nSYSTEM You are TITAN, an intelligent task automation agent. You help users accomplish complex tasks by selecting and using the right tools efficiently.\n`;\n\n writeFileSync(modelfilePath, modelfileContent, 'utf-8');\n\n // Try to create Ollama model\n try {\n logger.info(COMPONENT, `Creating Ollama model: ${modelName}`);\n execSync(`ollama create ${modelName} -f \"${modelfilePath}\"`, {\n stdio: 'pipe',\n timeout: 300_000, // 5 min timeout for model creation\n });\n\n // Optionally switch TITAN's model\n const autoDeploy = trainingConfig?.autoDeploy;\n let switchedModel = false;\n if (autoDeploy) {\n try {\n const { saveConfig } = await import('../../config/config.js');\n const currentConfig = loadConfig();\n currentConfig.agent.model = `ollama/${modelName}`;\n saveConfig(currentConfig);\n switchedModel = true;\n } catch (e) {\n logger.warn(COMPONENT, `Auto-deploy config update failed: ${(e as Error).message}`);\n }\n }\n\n // Update run\n results.status = 'deployed';\n results.deployedModel = `ollama/${modelName}`;\n writeFileSync(resultsPath, JSON.stringify(results, null, 2), 'utf-8');\n\n return [\n `## Model Deployed`,\n ``,\n `| Setting | Value |`,\n `|---------|-------|`,\n `| Model name | ${modelName} |`,\n `| Ollama ID | ollama/${modelName} |`,\n `| Base model | ${baseModel} |`,\n `| Final loss | ${results.final_loss} |`,\n `| Auto-switched | ${switchedModel ? 'yes' : 'no'} |`,\n ``,\n switchedModel\n ? `TITAN is now using \\`ollama/${modelName}\\` as its default model.`\n : `Model available as \\`ollama/${modelName}\\`. Use \\`/model ollama/${modelName}\\` to switch.`,\n ].join('\\n');\n } catch (err) {\n return `Error creating Ollama model: ${(err as Error).message}\\n\\nMake sure Ollama is running and accessible.`;\n }\n}\n\n// ── Cloud-Assisted Training Data Generation ──────────────────────────\n\nconst TITAN_SYSTEM_PROMPT = `You are TITAN (The Intelligent Task Automation Network), an autonomous AI agent framework. You help users accomplish complex tasks by selecting and executing tools efficiently. Always respond concisely and accurately. Use tools when appropriate — answer directly when you can.`;\n\nconst CLOUD_TRAINING_CATEGORIES: Record<string, { description: string; prompts: string[] }> = {\n tool_use: {\n description: 'Single and multi-tool usage with proper function calling format',\n prompts: [\n 'What is the weather in San Francisco right now?',\n 'Search the web for the latest news about AI agents',\n 'Read the file at /home/user/project/README.md',\n 'Create a new file called notes.txt with my meeting notes',\n 'Find all Python files in the current directory',\n 'Send an email to team@company.com with the weekly report',\n 'Check the disk usage on this machine',\n 'What time is it in Tokyo?',\n 'Search GitHub for repositories related to autonomous agents',\n 'Take a screenshot of https://news.ycombinator.com',\n 'Browse to https://example.com and extract the main heading',\n 'Run the command \"npm test\" and tell me if the tests pass',\n 'Look up the DNS records for anthropic.com',\n 'Download the file at https://example.com/data.csv',\n 'Check if port 8080 is in use on this machine',\n ],\n },\n reasoning: {\n description: 'Multi-step reasoning, planning, and problem decomposition',\n prompts: [\n 'I need to deploy a Node.js app to production. Walk me through the steps.',\n 'Compare PostgreSQL vs MongoDB for a real-time chat application',\n 'My API is returning 500 errors intermittently. How should I debug this?',\n 'Design a caching strategy for an e-commerce product catalog',\n 'What are the tradeoffs between microservices and a monolith for a startup?',\n 'Plan a migration from REST to GraphQL for an existing API',\n 'How do I set up CI/CD for a TypeScript project with GitHub Actions?',\n 'Explain the CAP theorem and how it applies to distributed databases',\n 'What is the best approach to handle rate limiting in a public API?',\n 'How should I structure a React app with 50+ components?',\n ],\n },\n coding: {\n description: 'Code generation, debugging, and refactoring',\n prompts: [\n 'Write a TypeScript function that retries a fetch request with exponential backoff',\n 'Debug this code: const result = await fetch(url); const data = result.json();',\n 'Refactor this function to use async/await instead of .then() chains',\n 'Write a Python script to process a CSV file and output JSON',\n 'Create a React hook that debounces user input',\n 'Write a SQL query to find the top 10 customers by total spend',\n 'Implement a simple LRU cache in TypeScript',\n 'Write a bash script that monitors disk usage and alerts if over 90%',\n 'Create an Express middleware for request logging with timestamps',\n 'Write unit tests for a function that validates email addresses',\n ],\n },\n research: {\n description: 'Web research, information gathering, and synthesis',\n prompts: [\n 'Research the current state of autonomous AI agent frameworks in 2026',\n 'Find the top 5 competitors to TITAN and compare their features',\n 'What are the latest developments in LoRA fine-tuning techniques?',\n 'Summarize the key findings from the latest GPT-5 benchmarks',\n 'Research best practices for securing a Node.js production server',\n 'What are the most popular MCP servers available right now?',\n 'Find recent papers on multi-agent orchestration systems',\n 'Research the current pricing for cloud GPU instances for AI training',\n ],\n },\n conversation: {\n description: 'Natural dialogue, follow-ups, and context maintenance',\n prompts: [\n 'Hey TITAN, what can you do?',\n 'Tell me about yourself',\n 'What tools do you have available?',\n 'Can you help me with my project?',\n 'Thanks for your help earlier with the deployment',\n 'I changed my mind about the previous request, can we try a different approach?',\n 'What was the last thing we worked on?',\n 'How many tools do you have loaded right now?',\n ],\n },\n error_recovery: {\n description: 'Handling errors, failed tools, and graceful degradation',\n prompts: [\n 'The web_search tool just returned an error. Can you try a different approach?',\n 'I got a timeout when trying to fetch that URL. What should we do?',\n 'The file I asked you to read does not exist. Can you help me find it?',\n 'The shell command failed with exit code 1. What went wrong?',\n 'Ollama is not responding. Can you still help me?',\n 'The API returned a 429 rate limit error. How do we handle this?',\n 'My browser automation script is failing because the page layout changed',\n 'The database connection timed out. What are our options?',\n ],\n },\n};\n\nasync function callOllamaCloud(model: string, systemPrompt: string, userPrompt: string): Promise<string> {\n const cfg = loadConfig();\n const ollamaUrl = cfg.providers.ollama?.baseUrl || process.env.OLLAMA_HOST || 'http://localhost:11434';\n try {\n const resp = await fetch(`${ollamaUrl}/api/chat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n stream: false,\n options: { temperature: 0.8, num_predict: 1024 },\n }),\n signal: AbortSignal.timeout(120_000),\n });\n if (!resp.ok) throw new Error(`Ollama returned ${resp.status}`);\n const data = await resp.json() as { message?: { content?: string } };\n return data.message?.content || '';\n } catch (err) {\n logger.warn(COMPONENT, `Cloud model call failed: ${(err as Error).message}`);\n return '';\n }\n}\n\nasync function trainGenerateCloud(args: Record<string, unknown>): Promise<string> {\n const teacherModel = (args.teacherModel as string) || 'qwen3.5:397b-cloud';\n const totalCount = (args.count as number) || 200;\n const appendMode = args.append !== false; // default true\n const categoryFilter = args.categories\n ? (args.categories as string).split(',').map(c => c.trim())\n : Object.keys(CLOUD_TRAINING_CATEGORIES);\n\n ensureDirs();\n\n logger.info(COMPONENT, `Cloud training data generation: teacher=${teacherModel}, count=${totalCount}, categories=${categoryFilter.join(',')}`);\n emitProgress({ type: 'info', phase: 'generate', message: `Starting cloud training data generation`, detail: { model: teacherModel, total: totalCount } });\n\n // Validate teacher model is accessible\n emitProgress({ type: 'info', phase: 'generate', message: `Testing connection to ${teacherModel}...` });\n const testResp = await callOllamaCloud(teacherModel, 'You are a helpful assistant.', 'Say OK');\n if (!testResp) {\n emitProgress({ type: 'error', phase: 'generate', message: `Cannot reach teacher model \"${teacherModel}\"` });\n return `Error: Cannot reach teacher model \"${teacherModel}\". Make sure it is pulled in Ollama.\\nAvailable cloud models: qwen3.5:397b-cloud, nemotron-3-super:cloud, qwen3-coder-next:cloud, glm-5:cloud, kimi-k2.5:cloud, gemini-3-flash-preview`;\n }\n emitProgress({ type: 'success', phase: 'generate', message: `Connected to ${teacherModel}` });\n\n const activeCategories = categoryFilter.filter(c => c in CLOUD_TRAINING_CATEGORIES);\n if (activeCategories.length === 0) {\n return `No valid categories. Choose from: ${Object.keys(CLOUD_TRAINING_CATEGORIES).join(', ')}`;\n }\n\n const perCategory = Math.ceil(totalCount / activeCategories.length);\n let totalGenerated = 0;\n let totalFailed = 0;\n const stats: Record<string, { generated: number; failed: number }> = {};\n\n // Write incrementally to disk so data survives tool timeouts\n const jsonlPath = join(TRAINING_DIR, 'train.jsonl');\n const valPath = join(TRAINING_DIR, 'val.jsonl');\n\n // If not appending, clear existing files\n if (!appendMode) {\n writeFileSync(jsonlPath, '', 'utf-8');\n writeFileSync(valPath, '', 'utf-8');\n }\n\n for (const catName of activeCategories) {\n const cat = CLOUD_TRAINING_CATEGORIES[catName];\n stats[catName] = { generated: 0, failed: 0 };\n const prompts = cat.prompts;\n\n logger.info(COMPONENT, `Generating ${perCategory} \"${catName}\" examples with ${teacherModel}...`);\n emitProgress({ type: 'info', phase: 'generate', message: `Starting category: ${catName}`, detail: { category: catName, current: 0, total: perCategory } });\n\n for (let i = 0; i < perCategory; i++) {\n // Pick a prompt (cycle through available, then ask teacher to generate new ones)\n let userPrompt: string;\n if (i < prompts.length) {\n userPrompt = prompts[i];\n } else {\n // Ask teacher to generate a novel prompt for this category\n const novelPrompt = await callOllamaCloud(teacherModel,\n 'You generate diverse, realistic user prompts for an AI agent assistant. Output ONLY the user prompt, nothing else.',\n `Generate a unique, realistic user prompt for the category \"${catName}\" (${cat.description}). Make it different from these existing ones:\\n${prompts.slice(0, 5).join('\\n')}\\n\\nOutput only the prompt text.`,\n );\n userPrompt = novelPrompt.trim() || prompts[i % prompts.length];\n }\n\n // Generate the ideal TITAN response from the teacher\n const teacherSystemPrompt = `${TITAN_SYSTEM_PROMPT}\n\nYou are generating a training example for the TITAN agent. Respond exactly as TITAN should respond to this user message. Be concise, helpful, and use the appropriate approach:\n- If the task requires a tool, describe what tool you would use and how\n- If you can answer directly, give a clear, accurate response\n- Show your reasoning for complex questions\n- Be practical and action-oriented`;\n\n const response = await callOllamaCloud(teacherModel, teacherSystemPrompt, userPrompt);\n\n if (response && response.length > 20) {\n const example = {\n messages: [\n { role: 'system', content: TITAN_SYSTEM_PROMPT },\n { role: 'user', content: userPrompt },\n { role: 'assistant', content: response },\n ],\n };\n // Write immediately to disk (incremental — survives timeouts)\n appendFileSync(jsonlPath, JSON.stringify(example) + '\\n', 'utf-8');\n // Every 10th example also goes to validation set\n if (totalGenerated % 10 === 0) {\n appendFileSync(valPath, JSON.stringify(example) + '\\n', 'utf-8');\n }\n stats[catName].generated++;\n totalGenerated++;\n emitProgress({\n type: 'progress', phase: 'generate',\n message: `Generated example ${totalGenerated}/${totalCount}`,\n detail: { category: catName, current: totalGenerated, total: totalCount, pct: Math.round((totalGenerated / totalCount) * 100), examples: totalGenerated },\n });\n } else {\n stats[catName].failed++;\n totalFailed++;\n emitProgress({ type: 'error', phase: 'generate', message: `Failed to generate example (${catName} #${i + 1})`, detail: { category: catName } });\n }\n\n // Log progress periodically\n if (i > 0 && i % 10 === 0) {\n logger.info(COMPONENT, ` ${catName}: ${i}/${perCategory} generated (${totalGenerated} total on disk)...`);\n }\n }\n }\n\n if (totalGenerated === 0) {\n return 'Error: No training examples generated. Check teacher model connectivity.';\n }\n\n // Count total lines in training file\n const totalLines = readFileSync(jsonlPath, 'utf-8').split('\\n').filter(l => l.trim()).length;\n\n // Build report\n const lines = [\n `## Cloud-Assisted Training Data Generated`,\n ``,\n `| Setting | Value |`,\n `|---------|-------|`,\n `| Teacher model | ${teacherModel} |`,\n `| Examples generated | ${totalGenerated} |`,\n `| Failed | ${totalFailed} |`,\n `| Total in file | ${totalLines} |`,\n `| Mode | ${appendMode ? 'append' : 'overwrite'} |`,\n `| Output | \\`${jsonlPath}\\` |`,\n ``,\n `### By Category`,\n `| Category | Generated | Failed |`,\n `|----------|-----------|--------|`,\n ];\n\n for (const [cat, s] of Object.entries(stats)) {\n lines.push(`| ${cat} | ${s.generated} | ${s.failed} |`);\n }\n\n lines.push('');\n lines.push(`Ready to fine-tune. Use \\`train_start\\` to begin LoRA training on the local GPU.`);\n\n emitProgress({\n type: 'complete', phase: 'generate',\n message: `Cloud training data generation complete: ${totalGenerated} examples (${totalLines} total in file)`,\n detail: { examples: totalGenerated, total: totalCount },\n });\n\n return lines.join('\\n');\n}\n\n// ── Registration ─────────────────────────────────────────────────────\n\nexport function registerModelTrainerSkill(): void {\n\n registerSkill(\n {\n name: 'model_trainer',\n description: 'Use this when the user says \"train on this\", \"fine-tune with these examples\", \"add this to training data\", \"teach yourself from our conversation\", or wants to improve the local model using past interactions. Runs LoRA fine-tuning on the RTX 5090 GPU.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'train_prepare',\n description: 'Prepare training data before launching a fine-tune. Use this when asked to \"get the training data ready\", \"collect examples\", or \"prepare for training\". Scans TITAN\\'s conversation history, extracts high-quality instruction/response pairs scored by tool success rates, and saves as JSONL ready for fine-tuning.',\n parameters: {\n type: 'object',\n properties: {\n minSamples: {\n type: 'number',\n description: 'Minimum training samples required before proceeding (default: 50)',\n },\n },\n required: [],\n },\n execute: trainPrepare,\n },\n );\n\n registerSkill(\n {\n name: 'model_trainer',\n description: 'Use this when the user says \"train on this\", \"fine-tune with these examples\", \"add this to training data\", \"teach yourself from our conversation\", or wants to improve the local model using past interactions. Runs LoRA fine-tuning on the RTX 5090 GPU.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'train_start',\n description: 'Start a LoRA fine-tuning job on the local GPU. Use when the user says \"start training\", \"fine-tune the model\", or \"run the training\". Requires training data to be prepared first (train_prepare). Runs as a background process on the RTX 5090.',\n parameters: {\n type: 'object',\n properties: {\n baseModel: {\n type: 'string',\n description: 'Base model to fine-tune (default: from config, e.g. qwen3.5:35b)',\n },\n method: {\n type: 'string',\n description: 'Training method: lora, qlora, or full (default: lora)',\n },\n budgetMinutes: {\n type: 'number',\n description: 'Training time budget in minutes (default: 30)',\n },\n epochs: {\n type: 'number',\n description: 'Number of training epochs (default: 3)',\n },\n },\n required: [],\n },\n execute: trainStart,\n },\n );\n\n registerSkill(\n {\n name: 'model_trainer',\n description: 'Use this when the user says \"train on this\", \"fine-tune with these examples\", \"add this to training data\", \"teach yourself from our conversation\", or wants to improve the local model using past interactions. Runs LoRA fine-tuning on the RTX 5090 GPU.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'train_status',\n description: 'Check how a training job is progressing. Use when asked \"how is training going?\", \"what\\'s the loss?\", \"is training done?\". Shows log output, current loss, and completion status. Without a runId, lists all training runs.',\n parameters: {\n type: 'object',\n properties: {\n runId: {\n type: 'string',\n description: 'Specific training run ID to check (optional — omit to list all runs)',\n },\n },\n required: [],\n },\n execute: trainStatus,\n },\n );\n\n registerSkill(\n {\n name: 'model_trainer',\n description: 'Use this when the user says \"train on this\", \"fine-tune with these examples\", \"add this to training data\", \"teach yourself from our conversation\", or wants to improve the local model using past interactions. Runs LoRA fine-tuning on the RTX 5090 GPU.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'train_deploy',\n description: 'Deploy a completed fine-tuned model into Ollama so TITAN can use it. Use when training is done and the user says \"deploy the model\", \"use the trained model\", or \"switch to the fine-tuned version\".',\n parameters: {\n type: 'object',\n properties: {\n runId: {\n type: 'string',\n description: 'Training run ID to deploy (from train_status)',\n },\n modelName: {\n type: 'string',\n description: 'Name for the deployed Ollama model (default: titan-custom)',\n },\n },\n required: ['runId'],\n },\n execute: trainDeploy,\n },\n );\n\n // ── Cloud-Assisted Training Data Generation ──────────────────────\n\n registerSkill(\n {\n name: 'model_trainer',\n description: 'Use this when the user says \"train on this\", \"fine-tune with these examples\", \"add this to training data\", \"teach yourself from our conversation\", or wants to improve the local model using past interactions. Runs LoRA fine-tuning on the RTX 5090 GPU.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'train_generate_cloud',\n description: 'Generate synthetic training examples using a large cloud model as teacher. Use when the user says \"generate training data\", \"create examples for fine-tuning\", or \"use the cloud model to teach the local model\". A smart cloud model (e.g. qwen3.5:397b-cloud) produces diverse, high-quality agent examples across tool use, reasoning, coding, research, conversation, and error recovery.',\n parameters: {\n type: 'object',\n properties: {\n teacherModel: {\n type: 'string',\n description: 'Cloud model to use as teacher (default: qwen3.5:397b-cloud)',\n },\n count: {\n type: 'number',\n description: 'Number of training examples to generate (default: 200)',\n },\n categories: {\n type: 'string',\n description: 'Which categories to generate: tool_use, reasoning, coding, research, conversation, error_recovery (default: all)',\n },\n append: {\n type: 'boolean',\n description: 'Append to existing training data instead of overwriting (default: true)',\n },\n },\n required: [],\n },\n execute: trainGenerateCloud,\n },\n );\n\n logger.info(COMPONENT, 'Model trainer skill registered (5 tools)');\n}\n"],"mappings":"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","names":["lines"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/skills/builtin/model_trainer.ts"],"sourcesContent":["/**\n * TITAN — Model Trainer Skill (Built-in)\n * Fine-tunes local Ollama models on TITAN's own conversation history\n * using the GPU (RTX 5090) on Titan PC.\n *\n * Pipeline: prepare training data → launch LoRA fine-tune → poll progress → deploy to Ollama\n */\nimport { registerSkill } from '../registry.js';\nimport { loadConfig } from '../../config/config.js';\nimport logger from '../../utils/logger.js';\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, appendFileSync } from 'fs';\nimport { join } from 'path';\nimport { TITAN_HOME } from '../../utils/constants.js';\nimport { execSync } from 'child_process';\nimport { EventEmitter } from 'events';\n\nconst COMPONENT = 'ModelTrainer';\n\n// ── Training Progress Events ─────────────────────────────────────────\nexport interface TrainingProgressEvent {\n type: 'info' | 'progress' | 'success' | 'error' | 'complete';\n phase: 'generate' | 'train' | 'deploy' | 'prepare';\n message: string;\n timestamp: string;\n detail?: {\n category?: string;\n current?: number;\n total?: number;\n pct?: number;\n model?: string;\n loss?: number;\n examples?: number;\n };\n}\n\nexport const trainingEvents = new EventEmitter();\ntrainingEvents.setMaxListeners(50);\n\nfunction emitProgress(event: Omit<TrainingProgressEvent, 'timestamp'>): void {\n const full: TrainingProgressEvent = { ...event, timestamp: new Date().toISOString() };\n trainingEvents.emit('progress', full);\n // Also persist to a rolling log file for the UI to poll as fallback\n try {\n const logPath = join(TITAN_HOME, 'training-progress.jsonl');\n appendFileSync(logPath, JSON.stringify(full) + '\\n', 'utf-8');\n } catch { /* best-effort */ }\n}\n\n// ── Paths ────────────────────────────────────────────────────────────\nconst TRAINING_DIR = join(TITAN_HOME, 'training-data');\nconst TRAINING_RUNS_DIR = join(TITAN_HOME, 'training-runs');\nconst TRAINING_HISTORY_PATH = join(TITAN_HOME, 'training-history.jsonl');\n\n// ── Types ────────────────────────────────────────────────────────────\n\ninterface TrainingRun {\n id: string;\n status: 'preparing' | 'training' | 'completed' | 'failed' | 'deploying';\n startedAt: string;\n completedAt?: string;\n baseModel: string;\n method: string;\n dataPoints: number;\n epochs?: number;\n finalLoss?: number;\n outputModel?: string;\n error?: string;\n}\n\ninterface TrainingDataPoint {\n instruction: string;\n response: string;\n toolsUsed?: string[];\n score?: number;\n}\n\n// ── Active training tracking ─────────────────────────────────────────\nconst activeRuns: Map<string, TrainingRun> = new Map();\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nfunction ensureDirs(): void {\n for (const dir of [TRAINING_DIR, TRAINING_RUNS_DIR]) {\n mkdirSync(dir, { recursive: true });\n }\n}\n\nfunction getRunId(): string {\n return `train-${Date.now().toString(36)}`;\n}\n\nfunction _appendTrainingHistory(run: TrainingRun): void {\n ensureDirs();\n appendFileSync(TRAINING_HISTORY_PATH, JSON.stringify(run) + '\\n', 'utf-8');\n}\n\nfunction _readTrainingHistory(limit: number = 20): TrainingRun[] {\n if (!existsSync(TRAINING_HISTORY_PATH)) return [];\n try {\n const lines = readFileSync(TRAINING_HISTORY_PATH, 'utf-8').split('\\n').filter(l => l.trim());\n const runs = lines.map(l => {\n try { return JSON.parse(l) as TrainingRun; }\n catch { return null; }\n }).filter(Boolean) as TrainingRun[];\n return runs.slice(-limit);\n } catch {\n return [];\n }\n}\nvoid _appendTrainingHistory;\nvoid _readTrainingHistory;\n\n/** Extract high-quality training pairs from TITAN's session database */\nfunction extractTrainingData(): TrainingDataPoint[] {\n const dataPoints: TrainingDataPoint[] = [];\n\n // Try to read from session history files\n const sessionsDir = join(TITAN_HOME, 'sessions');\n if (!existsSync(sessionsDir)) {\n logger.warn(COMPONENT, 'No sessions directory found');\n return dataPoints;\n }\n\n try {\n const files = readdirSync(sessionsDir).filter(f => f.endsWith('.json'));\n for (const file of files.slice(-100)) { // Last 100 sessions\n try {\n const sessionData = JSON.parse(readFileSync(join(sessionsDir, file), 'utf-8'));\n const messages = sessionData.messages || sessionData.history || [];\n\n for (let i = 0; i < messages.length - 1; i++) {\n const msg = messages[i];\n const nextMsg = messages[i + 1];\n\n if (msg.role === 'user' && nextMsg.role === 'assistant' && nextMsg.content) {\n // Only include substantive exchanges\n if (msg.content.length > 10 && nextMsg.content.length > 20) {\n dataPoints.push({\n instruction: msg.content,\n response: nextMsg.content,\n toolsUsed: nextMsg.toolsUsed || [],\n });\n }\n }\n }\n } catch {\n // Skip corrupt session files\n }\n }\n } catch (e) {\n logger.warn(COMPONENT, `Error reading sessions: ${(e as Error).message}`);\n }\n\n // Also read from learning data for quality signals\n const learningPath = join(TITAN_HOME, 'learning.json');\n if (existsSync(learningPath)) {\n try {\n const learning = JSON.parse(readFileSync(learningPath, 'utf-8'));\n // Use tool success rates to score training data quality\n if (learning.toolSuccessRates) {\n // Boost data points that used successful tools\n for (const dp of dataPoints) {\n if (dp.toolsUsed && dp.toolsUsed.length > 0) {\n const avgRate = dp.toolsUsed.reduce((sum: number, tool: string) => {\n const rate = learning.toolSuccessRates[tool];\n return sum + (rate?.successRate || 0.5);\n }, 0) / dp.toolsUsed.length;\n dp.score = avgRate;\n }\n }\n }\n } catch {\n // Ignore learning data errors\n }\n }\n\n // Sort by quality score (higher is better) and filter\n return dataPoints\n .sort((a, b) => (b.score || 0.5) - (a.score || 0.5))\n .filter(dp => (dp.score || 0.5) >= 0.4); // Exclude low-quality pairs\n}\n\n// ── Tool implementations ─────────────────────────────────────────────\n\nasync function trainPrepare(args: Record<string, unknown>): Promise<string> {\n const minSamples = (args.minSamples as number) || 50;\n\n ensureDirs();\n\n logger.info(COMPONENT, 'Extracting training data from session history...');\n const dataPoints = extractTrainingData();\n\n if (dataPoints.length < minSamples) {\n return `Not enough high-quality training data. Found ${dataPoints.length} pairs, need at least ${minSamples}. Keep using TITAN to build up more conversation history.`;\n }\n\n // Format as JSONL for training\n const jsonlPath = join(TRAINING_DIR, 'train.jsonl');\n const jsonlContent = dataPoints.map(dp => JSON.stringify({\n messages: [\n { role: 'user', content: dp.instruction },\n { role: 'assistant', content: dp.response },\n ],\n })).join('\\n');\n\n writeFileSync(jsonlPath, jsonlContent, 'utf-8');\n\n // Create a validation split (10%)\n const valSize = Math.max(1, Math.floor(dataPoints.length * 0.1));\n const valData = dataPoints.slice(0, valSize);\n const valPath = join(TRAINING_DIR, 'val.jsonl');\n writeFileSync(valPath, valData.map(dp => JSON.stringify({\n messages: [\n { role: 'user', content: dp.instruction },\n { role: 'assistant', content: dp.response },\n ],\n })).join('\\n'), 'utf-8');\n\n return [\n `## Training Data Prepared`,\n ``,\n `| Stat | Value |`,\n `|------|-------|`,\n `| Total pairs | ${dataPoints.length} |`,\n `| Training set | ${dataPoints.length - valSize} |`,\n `| Validation set | ${valSize} |`,\n `| Avg quality score | ${(dataPoints.reduce((s, d) => s + (d.score || 0.5), 0) / dataPoints.length).toFixed(2)} |`,\n ``,\n `Training data: \\`${jsonlPath}\\``,\n `Validation data: \\`${valPath}\\``,\n ``,\n `Ready to train. Use \\`train_start\\` to begin fine-tuning.`,\n ].join('\\n');\n}\n\nasync function trainStart(args: Record<string, unknown>): Promise<string> {\n const config = loadConfig();\n const trainingConfig = (config as Record<string, unknown>).training as Record<string, unknown> | undefined;\n\n if (trainingConfig && trainingConfig.enabled === false) {\n return 'Training is disabled in config. Set training.enabled = true to enable.';\n }\n\n // Model resolution: explicit arg → config → active model (if local/ollama) → fallback\n const activeModel = config.agent?.model || '';\n const activeModelName = activeModel.replace(/^ollama\\//, '');\n const isLocalModel = activeModel.startsWith('ollama/') || (!activeModel.includes('/') && activeModel.length > 0);\n const baseModel = (args.baseModel as string)\n || (trainingConfig?.baseModel as string)\n || (isLocalModel ? activeModelName : '')\n || 'qwen3.5:35b';\n const method = (args.method as string) || (trainingConfig?.method as string) || 'lora';\n const budgetMinutes = (args.budgetMinutes as number) || (trainingConfig?.budgetMinutes as number) || 30;\n const epochs = (args.epochs as number) || 3;\n\n const trainDataPath = join(TRAINING_DIR, 'train.jsonl');\n if (!existsSync(trainDataPath)) {\n return 'No training data found. Run `train_prepare` first.';\n }\n\n const dataLines = readFileSync(trainDataPath, 'utf-8').split('\\n').filter(l => l.trim());\n if (dataLines.length < 10) {\n return `Only ${dataLines.length} training samples — need at least 10. Run \\`train_prepare\\` to extract more data.`;\n }\n\n const runId = getRunId();\n const runDir = join(TRAINING_RUNS_DIR, runId);\n mkdirSync(runDir, { recursive: true });\n\n const run: TrainingRun = {\n id: runId,\n status: 'training',\n startedAt: new Date().toISOString(),\n baseModel,\n method,\n dataPoints: dataLines.length,\n epochs,\n };\n activeRuns.set(runId, run);\n\n // Generate training script\n const trainScript = `#!/usr/bin/env python3\n\"\"\"TITAN Auto-Training Script — LoRA fine-tuning via unsloth\"\"\"\nimport os, sys, json, time, torch\n\n# Check if unsloth is available\ntry:\n from unsloth import FastLanguageModel\n from trl import SFTTrainer\n from transformers import TrainingArguments\n from datasets import load_dataset\n HAS_UNSLOTH = True\nexcept ImportError:\n HAS_UNSLOTH = False\n print(\"WARNING: unsloth not installed. Install with: pip install unsloth\")\n print(\"Falling back to simulation mode for testing.\")\n\nOUTPUT_DIR = \"${runDir}\"\nDATA_PATH = \"${trainDataPath}\"\nBASE_MODEL = \"${baseModel}\"\nEPOCHS = ${epochs}\nMAX_MINUTES = ${budgetMinutes}\n\n# Map Ollama model names to HuggingFace model IDs\nOLLAMA_TO_HF = {\n \"qwen3.5:35b\": \"Qwen/Qwen3.5-9B\", # 35B MoE too large for single GPU — use 9B dense\n \"qwen3.5:7b\": \"Qwen/Qwen3.5-7B\",\n \"qwen3:30b\": \"Qwen/Qwen3-30B-A3B\",\n \"qwen3:8b\": \"Qwen/Qwen3-8B\",\n \"llama3.1:8b\": \"meta-llama/Llama-3.1-8B-Instruct\",\n \"llama3.1:70b\": \"meta-llama/Llama-3.1-70B-Instruct\",\n \"mistral:7b\": \"mistralai/Mistral-7B-Instruct-v0.3\",\n \"gemma2:9b\": \"google/gemma-2-9b-it\",\n \"phi3:14b\": \"microsoft/Phi-3-medium-128k-instruct\",\n \"devstral-small-2\": \"mistralai/Devstral-Small-2505\",\n}\n\ndef resolve_model_name(name):\n \"\"\"Resolve Ollama model name to HuggingFace model ID.\"\"\"\n if name in OLLAMA_TO_HF:\n hf_name = OLLAMA_TO_HF[name]\n print(f\"Resolved Ollama model '{name}' -> HuggingFace '{hf_name}'\")\n return hf_name\n # If it looks like a HF model (contains /), use as-is\n if \"/\" in name:\n return name\n print(f\"WARNING: Unknown model '{name}', trying as HuggingFace ID directly\")\n return name\n\ndef main():\n start = time.time()\n\n if not HAS_UNSLOTH:\n # Simulation mode — still produces a evaluable val_score so the\n # autopilot can benchmark improvements. Score is derived from data\n # quality (line count, JSON validity) rather than fake constants.\n import random\n print(f\"[SIM] Loading base model: {BASE_MODEL}\")\n print(f\"[SIM] Training data: {DATA_PATH}\")\n time.sleep(2)\n\n # Compute a data-quality heuristic that stays stable across runs\n data_score = 0.0\n try:\n with open(DATA_PATH, \"r\") as f:\n lines = f.readlines()\n total = len(lines)\n valid_json = sum(1 for line in lines if line.strip() and line.strip().startswith(\"{\"))\n data_score = min(100.0, (valid_json / max(total, 1)) * 100.0 + total * 0.5)\n except Exception:\n data_score = 45.0\n\n best_loss = 999.0\n for epoch in range(1, EPOCHS + 1):\n elapsed = time.time() - start\n if elapsed > MAX_MINUTES * 60:\n print(f\"[SIM] Time budget exhausted at epoch {epoch}\")\n break\n loss = max(0.5, 2.5 - (epoch * 0.3) - (data_score / 100.0))\n best_loss = min(best_loss, loss)\n print(f\"Epoch {epoch}/{EPOCHS} — loss: {loss:.4f}\")\n time.sleep(1)\n\n # val_score = inverse of loss, scaled to 0-100, boosted by data quality\n val_score = round(min(100.0, max(0.0, (3.0 - best_loss) * 40.0 + data_score * 0.2)), 2)\n\n # Write results\n with open(os.path.join(OUTPUT_DIR, \"results.json\"), \"w\") as f:\n json.dump({\n \"status\": \"simulated\",\n \"epochs\": EPOCHS,\n \"final_loss\": round(best_loss, 4),\n \"val_score\": val_score,\n \"model_path\": None,\n \"note\": \"unsloth not installed — score is a data-quality heuristic\"\n }, f)\n print(f\"Training complete (simulated) — val_score: {val_score}\")\n return\n\n # Real training with unsloth\n hf_model = resolve_model_name(BASE_MODEL)\n print(f\"Loading base model: {hf_model}\")\n model, tokenizer = FastLanguageModel.from_pretrained(\n model_name=hf_model,\n max_seq_length=2048,\n dtype=None, # Auto-detect\n load_in_4bit=True,\n )\n\n model = FastLanguageModel.get_peft_model(\n model,\n r=16,\n target_modules=[\"q_proj\", \"k_proj\", \"v_proj\", \"o_proj\", \"gate_proj\", \"up_proj\", \"down_proj\"],\n lora_alpha=16,\n lora_dropout=0,\n bias=\"none\",\n use_gradient_checkpointing=\"unsloth\",\n )\n\n dataset = load_dataset(\"json\", data_files=DATA_PATH, split=\"train\")\n print(f\"Loaded {len(dataset)} training examples\")\n\n # Format chat messages into text using the tokenizer's chat template\n def format_chat(example):\n text = tokenizer.apply_chat_template(example[\"messages\"], tokenize=False, add_generation_prompt=False)\n return {\"text\": text}\n\n dataset = dataset.map(format_chat)\n print(f\"Formatted dataset — sample length: {len(dataset[0]['text'])} chars\")\n\n trainer = SFTTrainer(\n model=model,\n tokenizer=tokenizer,\n train_dataset=dataset,\n dataset_text_field=\"text\",\n max_seq_length=2048,\n packing=True,\n args=TrainingArguments(\n output_dir=OUTPUT_DIR,\n per_device_train_batch_size=${(args.batchSize as number) || 4},\n gradient_accumulation_steps=4,\n num_train_epochs=EPOCHS,\n learning_rate=${(args.learningRate as number) || 2e-4},\n fp16=False,\n bf16=True,\n logging_steps=1,\n save_strategy=\"epoch\",\n warmup_steps=10,\n weight_decay=0.01,\n lr_scheduler_type=\"cosine\",\n max_steps=-1,\n report_to=\"none\",\n ),\n )\n\n print(\"Starting training...\")\n gpu_stats = torch.cuda.get_device_properties(0)\n print(f\"GPU: {gpu_stats.name}, VRAM: {gpu_stats.total_memory / 1024**3:.1f} GB\")\n result = trainer.train()\n\n # Save LoRA adapter\n adapter_dir = os.path.join(OUTPUT_DIR, \"lora_adapter\")\n model.save_pretrained(adapter_dir)\n tokenizer.save_pretrained(adapter_dir)\n print(f\"LoRA adapter saved to {adapter_dir}\")\n\n # Also save as merged GGUF for Ollama\n print(\"Saving merged model as GGUF (Q4_K_M)...\")\n try:\n model.save_pretrained_gguf(\n os.path.join(OUTPUT_DIR, \"gguf\"),\n tokenizer,\n quantization_method=\"q4_k_m\",\n )\n print(\"GGUF export complete\")\n except Exception as e:\n print(f\"GGUF export failed (non-fatal): {e}\")\n\n with open(os.path.join(OUTPUT_DIR, \"results.json\"), \"w\") as f:\n json.dump({\n \"status\": \"completed\",\n \"epochs\": EPOCHS,\n \"final_loss\": result.training_loss,\n \"model_path\": adapter_dir,\n \"gguf_path\": os.path.join(OUTPUT_DIR, \"gguf\"),\n }, f)\n\n print(f\"Training complete — loss: {result.training_loss:.4f}\")\n\nif __name__ == \"__main__\":\n main()\n`;\n\n const scriptPath = join(runDir, 'train.py');\n writeFileSync(scriptPath, trainScript, 'utf-8');\n\n // Launch training as background process\n try {\n // Prefer venv python (has unsloth installed), fall back to system python3\n const venvCandidates = [\n join(TITAN_HOME, 'venv', 'bin', 'python'), // ~/.titan/venv\n '/opt/TITAN/venv/bin/python', // production deploy\n ];\n const pythonBin = venvCandidates.find(p => existsSync(p)) ?? 'python3';\n execSync(`${pythonBin} \"${scriptPath}\" > \"${join(runDir, 'train.log')}\" 2>&1 &`, {\n stdio: 'pipe',\n timeout: 5000,\n });\n } catch {\n // Background process launch — may appear to \"fail\" but actually started\n logger.info(COMPONENT, 'Training process launched in background');\n }\n\n // Save run metadata\n writeFileSync(join(runDir, 'meta.json'), JSON.stringify(run, null, 2), 'utf-8');\n\n return [\n `## Training Started`,\n ``,\n `| Setting | Value |`,\n `|---------|-------|`,\n `| Run ID | ${runId} |`,\n `| Base model | ${baseModel} |`,\n `| Method | ${method} |`,\n `| Training samples | ${dataLines.length} |`,\n `| Epochs | ${epochs} |`,\n `| Budget | ${budgetMinutes} min |`,\n ``,\n `Training log: \\`${join(runDir, 'train.log')}\\``,\n `Use \\`train_status\\` to check progress.`,\n ].join('\\n');\n}\n\nasync function trainStatus(args: Record<string, unknown>): Promise<string> {\n const runId = args.runId as string | undefined;\n\n if (runId) {\n const runDir = join(TRAINING_RUNS_DIR, runId);\n if (!existsSync(runDir)) {\n return `Training run \"${runId}\" not found.`;\n }\n\n const logPath = join(runDir, 'train.log');\n const resultsPath = join(runDir, 'results.json');\n\n const lines: string[] = [`## Training Run: ${runId}\\n`];\n\n if (existsSync(resultsPath)) {\n const results = JSON.parse(readFileSync(resultsPath, 'utf-8'));\n lines.push(`**Status**: ${results.status}`);\n lines.push(`**Final loss**: ${results.final_loss}`);\n lines.push(`**Epochs**: ${results.epochs}`);\n if (results.model_path) {\n lines.push(`**Model path**: ${results.model_path}`);\n }\n } else if (existsSync(logPath)) {\n const log = readFileSync(logPath, 'utf-8');\n const lastLines = log.trim().split('\\n').slice(-10);\n lines.push('**Status**: training...\\n');\n lines.push('### Recent log output:');\n lines.push('```');\n lines.push(lastLines.join('\\n'));\n lines.push('```');\n } else {\n lines.push('**Status**: waiting to start...');\n }\n\n return lines.join('\\n');\n }\n\n // List all runs\n const lines: string[] = ['## Training Runs\\n'];\n\n if (existsSync(TRAINING_RUNS_DIR)) {\n const dirs = readdirSync(TRAINING_RUNS_DIR, { withFileTypes: true })\n .filter(d => d.isDirectory())\n .map(d => d.name)\n .sort()\n .reverse();\n\n if (dirs.length > 0) {\n lines.push('| Run ID | Base Model | Status | Loss | Samples |');\n lines.push('|--------|-----------|--------|------|---------|');\n\n for (const dir of dirs.slice(0, 10)) {\n const metaPath = join(TRAINING_RUNS_DIR, dir, 'meta.json');\n const resultsPath = join(TRAINING_RUNS_DIR, dir, 'results.json');\n\n if (existsSync(metaPath)) {\n const meta = JSON.parse(readFileSync(metaPath, 'utf-8'));\n let status = 'unknown';\n let loss = '-';\n\n if (existsSync(resultsPath)) {\n const results = JSON.parse(readFileSync(resultsPath, 'utf-8'));\n status = results.status || 'completed';\n loss = results.final_loss?.toFixed(4) || '-';\n } else {\n status = 'training...';\n }\n\n lines.push(`| ${dir} | ${meta.baseModel} | ${status} | ${loss} | ${meta.dataPoints} |`);\n }\n }\n } else {\n lines.push('No training runs found. Use `train_prepare` then `train_start` to begin.');\n }\n } else {\n lines.push('No training runs found.');\n }\n\n return lines.join('\\n');\n}\n\nasync function trainDeploy(args: Record<string, unknown>): Promise<string> {\n const runId = args.runId as string;\n const modelName = (args.modelName as string) || 'titan-custom';\n\n if (!runId) {\n return 'Error: runId is required. Use `train_status` to find completed runs.';\n }\n\n const runDir = join(TRAINING_RUNS_DIR, runId);\n const resultsPath = join(runDir, 'results.json');\n\n if (!existsSync(resultsPath)) {\n return `Training run \"${runId}\" not found or not completed.`;\n }\n\n const results = JSON.parse(readFileSync(resultsPath, 'utf-8'));\n if (results.status === 'simulated') {\n return [\n `## Deploy — Simulated Run`,\n ``,\n `This was a simulated training run (unsloth not installed).`,\n `To deploy a real model:`,\n `1. Install unsloth on Titan PC: \\`pip install unsloth\\``,\n `2. Run \\`train_start\\` again for real training`,\n `3. Then \\`train_deploy\\` to create an Ollama model`,\n ].join('\\n');\n }\n\n if (!results.model_path || !existsSync(results.model_path)) {\n return `Model adapter not found at ${results.model_path}. Training may not have completed successfully.`;\n }\n\n // Create Modelfile for Ollama\n const config = loadConfig();\n const trainingConfig = (config as Record<string, unknown>).training as Record<string, unknown> | undefined;\n // Use configured base model, or active local model, or fallback\n const activeModel = config.agent?.model || '';\n const activeModelName = activeModel.replace(/^ollama\\//, '');\n const isLocalModel = activeModel.startsWith('ollama/') || (!activeModel.includes('/') && activeModel.length > 0);\n const baseModel = (trainingConfig?.baseModel as string)\n || (isLocalModel ? activeModelName : '')\n || 'qwen3.5:35b';\n\n const modelfilePath = join(runDir, 'Modelfile');\n const modelfileContent = `FROM ${baseModel}\nADAPTER ${results.model_path}\n\nPARAMETER temperature 0.7\nPARAMETER num_ctx 65536\n\nSYSTEM You are TITAN, an intelligent task automation agent. You help users accomplish complex tasks by selecting and using the right tools efficiently.\n`;\n\n writeFileSync(modelfilePath, modelfileContent, 'utf-8');\n\n // Try to create Ollama model\n try {\n logger.info(COMPONENT, `Creating Ollama model: ${modelName}`);\n execSync(`ollama create ${modelName} -f \"${modelfilePath}\"`, {\n stdio: 'pipe',\n timeout: 300_000, // 5 min timeout for model creation\n });\n\n // Optionally switch TITAN's model\n const autoDeploy = trainingConfig?.autoDeploy;\n let switchedModel = false;\n if (autoDeploy) {\n try {\n const { saveConfig } = await import('../../config/config.js');\n const currentConfig = loadConfig();\n currentConfig.agent.model = `ollama/${modelName}`;\n saveConfig(currentConfig);\n switchedModel = true;\n } catch (e) {\n logger.warn(COMPONENT, `Auto-deploy config update failed: ${(e as Error).message}`);\n }\n }\n\n // Update run\n results.status = 'deployed';\n results.deployedModel = `ollama/${modelName}`;\n writeFileSync(resultsPath, JSON.stringify(results, null, 2), 'utf-8');\n\n return [\n `## Model Deployed`,\n ``,\n `| Setting | Value |`,\n `|---------|-------|`,\n `| Model name | ${modelName} |`,\n `| Ollama ID | ollama/${modelName} |`,\n `| Base model | ${baseModel} |`,\n `| Final loss | ${results.final_loss} |`,\n `| Auto-switched | ${switchedModel ? 'yes' : 'no'} |`,\n ``,\n switchedModel\n ? `TITAN is now using \\`ollama/${modelName}\\` as its default model.`\n : `Model available as \\`ollama/${modelName}\\`. Use \\`/model ollama/${modelName}\\` to switch.`,\n ].join('\\n');\n } catch (err) {\n return `Error creating Ollama model: ${(err as Error).message}\\n\\nMake sure Ollama is running and accessible.`;\n }\n}\n\n// ── Cloud-Assisted Training Data Generation ──────────────────────────\n\nconst TITAN_SYSTEM_PROMPT = `You are TITAN (The Intelligent Task Automation Network), an autonomous AI agent framework. You help users accomplish complex tasks by selecting and executing tools efficiently. Always respond concisely and accurately. Use tools when appropriate — answer directly when you can.`;\n\nconst CLOUD_TRAINING_CATEGORIES: Record<string, { description: string; prompts: string[] }> = {\n tool_use: {\n description: 'Single and multi-tool usage with proper function calling format',\n prompts: [\n 'What is the weather in San Francisco right now?',\n 'Search the web for the latest news about AI agents',\n 'Read the file at /home/user/project/README.md',\n 'Create a new file called notes.txt with my meeting notes',\n 'Find all Python files in the current directory',\n 'Send an email to team@company.com with the weekly report',\n 'Check the disk usage on this machine',\n 'What time is it in Tokyo?',\n 'Search GitHub for repositories related to autonomous agents',\n 'Take a screenshot of https://news.ycombinator.com',\n 'Browse to https://example.com and extract the main heading',\n 'Run the command \"npm test\" and tell me if the tests pass',\n 'Look up the DNS records for anthropic.com',\n 'Download the file at https://example.com/data.csv',\n 'Check if port 8080 is in use on this machine',\n ],\n },\n reasoning: {\n description: 'Multi-step reasoning, planning, and problem decomposition',\n prompts: [\n 'I need to deploy a Node.js app to production. Walk me through the steps.',\n 'Compare PostgreSQL vs MongoDB for a real-time chat application',\n 'My API is returning 500 errors intermittently. How should I debug this?',\n 'Design a caching strategy for an e-commerce product catalog',\n 'What are the tradeoffs between microservices and a monolith for a startup?',\n 'Plan a migration from REST to GraphQL for an existing API',\n 'How do I set up CI/CD for a TypeScript project with GitHub Actions?',\n 'Explain the CAP theorem and how it applies to distributed databases',\n 'What is the best approach to handle rate limiting in a public API?',\n 'How should I structure a React app with 50+ components?',\n ],\n },\n coding: {\n description: 'Code generation, debugging, and refactoring',\n prompts: [\n 'Write a TypeScript function that retries a fetch request with exponential backoff',\n 'Debug this code: const result = await fetch(url); const data = result.json();',\n 'Refactor this function to use async/await instead of .then() chains',\n 'Write a Python script to process a CSV file and output JSON',\n 'Create a React hook that debounces user input',\n 'Write a SQL query to find the top 10 customers by total spend',\n 'Implement a simple LRU cache in TypeScript',\n 'Write a bash script that monitors disk usage and alerts if over 90%',\n 'Create an Express middleware for request logging with timestamps',\n 'Write unit tests for a function that validates email addresses',\n ],\n },\n research: {\n description: 'Web research, information gathering, and synthesis',\n prompts: [\n 'Research the current state of autonomous AI agent frameworks in 2026',\n 'Find the top 5 competitors to TITAN and compare their features',\n 'What are the latest developments in LoRA fine-tuning techniques?',\n 'Summarize the key findings from the latest GPT-5 benchmarks',\n 'Research best practices for securing a Node.js production server',\n 'What are the most popular MCP servers available right now?',\n 'Find recent papers on multi-agent orchestration systems',\n 'Research the current pricing for cloud GPU instances for AI training',\n ],\n },\n conversation: {\n description: 'Natural dialogue, follow-ups, and context maintenance',\n prompts: [\n 'Hey TITAN, what can you do?',\n 'Tell me about yourself',\n 'What tools do you have available?',\n 'Can you help me with my project?',\n 'Thanks for your help earlier with the deployment',\n 'I changed my mind about the previous request, can we try a different approach?',\n 'What was the last thing we worked on?',\n 'How many tools do you have loaded right now?',\n ],\n },\n error_recovery: {\n description: 'Handling errors, failed tools, and graceful degradation',\n prompts: [\n 'The web_search tool just returned an error. Can you try a different approach?',\n 'I got a timeout when trying to fetch that URL. What should we do?',\n 'The file I asked you to read does not exist. Can you help me find it?',\n 'The shell command failed with exit code 1. What went wrong?',\n 'Ollama is not responding. Can you still help me?',\n 'The API returned a 429 rate limit error. How do we handle this?',\n 'My browser automation script is failing because the page layout changed',\n 'The database connection timed out. What are our options?',\n ],\n },\n};\n\nasync function callOllamaCloud(model: string, systemPrompt: string, userPrompt: string): Promise<string> {\n const cfg = loadConfig();\n const ollamaUrl = cfg.providers.ollama?.baseUrl || process.env.OLLAMA_HOST || 'http://localhost:11434';\n try {\n const resp = await fetch(`${ollamaUrl}/api/chat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n stream: false,\n options: { temperature: 0.8, num_predict: 1024 },\n }),\n signal: AbortSignal.timeout(120_000),\n });\n if (!resp.ok) throw new Error(`Ollama returned ${resp.status}`);\n const data = await resp.json() as { message?: { content?: string } };\n return data.message?.content || '';\n } catch (err) {\n logger.warn(COMPONENT, `Cloud model call failed: ${(err as Error).message}`);\n return '';\n }\n}\n\nasync function trainGenerateCloud(args: Record<string, unknown>): Promise<string> {\n const teacherModel = (args.teacherModel as string) || 'qwen3.5:397b-cloud';\n const totalCount = (args.count as number) || 200;\n const appendMode = args.append !== false; // default true\n const categoryFilter = args.categories\n ? (args.categories as string).split(',').map(c => c.trim())\n : Object.keys(CLOUD_TRAINING_CATEGORIES);\n\n ensureDirs();\n\n logger.info(COMPONENT, `Cloud training data generation: teacher=${teacherModel}, count=${totalCount}, categories=${categoryFilter.join(',')}`);\n emitProgress({ type: 'info', phase: 'generate', message: `Starting cloud training data generation`, detail: { model: teacherModel, total: totalCount } });\n\n // Validate teacher model is accessible\n emitProgress({ type: 'info', phase: 'generate', message: `Testing connection to ${teacherModel}...` });\n const testResp = await callOllamaCloud(teacherModel, 'You are a helpful assistant.', 'Say OK');\n if (!testResp) {\n emitProgress({ type: 'error', phase: 'generate', message: `Cannot reach teacher model \"${teacherModel}\"` });\n return `Error: Cannot reach teacher model \"${teacherModel}\". Make sure it is pulled in Ollama.\\nAvailable cloud models: qwen3.5:397b-cloud, nemotron-3-super:cloud, qwen3-coder-next:cloud, glm-5:cloud, kimi-k2.5:cloud, gemini-3-flash-preview`;\n }\n emitProgress({ type: 'success', phase: 'generate', message: `Connected to ${teacherModel}` });\n\n const activeCategories = categoryFilter.filter(c => c in CLOUD_TRAINING_CATEGORIES);\n if (activeCategories.length === 0) {\n return `No valid categories. Choose from: ${Object.keys(CLOUD_TRAINING_CATEGORIES).join(', ')}`;\n }\n\n const perCategory = Math.ceil(totalCount / activeCategories.length);\n let totalGenerated = 0;\n let totalFailed = 0;\n const stats: Record<string, { generated: number; failed: number }> = {};\n\n // Write incrementally to disk so data survives tool timeouts\n const jsonlPath = join(TRAINING_DIR, 'train.jsonl');\n const valPath = join(TRAINING_DIR, 'val.jsonl');\n\n // If not appending, clear existing files\n if (!appendMode) {\n writeFileSync(jsonlPath, '', 'utf-8');\n writeFileSync(valPath, '', 'utf-8');\n }\n\n for (const catName of activeCategories) {\n const cat = CLOUD_TRAINING_CATEGORIES[catName];\n stats[catName] = { generated: 0, failed: 0 };\n const prompts = cat.prompts;\n\n logger.info(COMPONENT, `Generating ${perCategory} \"${catName}\" examples with ${teacherModel}...`);\n emitProgress({ type: 'info', phase: 'generate', message: `Starting category: ${catName}`, detail: { category: catName, current: 0, total: perCategory } });\n\n for (let i = 0; i < perCategory; i++) {\n // Pick a prompt (cycle through available, then ask teacher to generate new ones)\n let userPrompt: string;\n if (i < prompts.length) {\n userPrompt = prompts[i];\n } else {\n // Ask teacher to generate a novel prompt for this category\n const novelPrompt = await callOllamaCloud(teacherModel,\n 'You generate diverse, realistic user prompts for an AI agent assistant. Output ONLY the user prompt, nothing else.',\n `Generate a unique, realistic user prompt for the category \"${catName}\" (${cat.description}). Make it different from these existing ones:\\n${prompts.slice(0, 5).join('\\n')}\\n\\nOutput only the prompt text.`,\n );\n userPrompt = novelPrompt.trim() || prompts[i % prompts.length];\n }\n\n // Generate the ideal TITAN response from the teacher\n const teacherSystemPrompt = `${TITAN_SYSTEM_PROMPT}\n\nYou are generating a training example for the TITAN agent. Respond exactly as TITAN should respond to this user message. Be concise, helpful, and use the appropriate approach:\n- If the task requires a tool, describe what tool you would use and how\n- If you can answer directly, give a clear, accurate response\n- Show your reasoning for complex questions\n- Be practical and action-oriented`;\n\n const response = await callOllamaCloud(teacherModel, teacherSystemPrompt, userPrompt);\n\n if (response && response.length > 20) {\n const example = {\n messages: [\n { role: 'system', content: TITAN_SYSTEM_PROMPT },\n { role: 'user', content: userPrompt },\n { role: 'assistant', content: response },\n ],\n };\n // Write immediately to disk (incremental — survives timeouts)\n appendFileSync(jsonlPath, JSON.stringify(example) + '\\n', 'utf-8');\n // Every 10th example also goes to validation set\n if (totalGenerated % 10 === 0) {\n appendFileSync(valPath, JSON.stringify(example) + '\\n', 'utf-8');\n }\n stats[catName].generated++;\n totalGenerated++;\n emitProgress({\n type: 'progress', phase: 'generate',\n message: `Generated example ${totalGenerated}/${totalCount}`,\n detail: { category: catName, current: totalGenerated, total: totalCount, pct: Math.round((totalGenerated / totalCount) * 100), examples: totalGenerated },\n });\n } else {\n stats[catName].failed++;\n totalFailed++;\n emitProgress({ type: 'error', phase: 'generate', message: `Failed to generate example (${catName} #${i + 1})`, detail: { category: catName } });\n }\n\n // Log progress periodically\n if (i > 0 && i % 10 === 0) {\n logger.info(COMPONENT, ` ${catName}: ${i}/${perCategory} generated (${totalGenerated} total on disk)...`);\n }\n }\n }\n\n if (totalGenerated === 0) {\n return 'Error: No training examples generated. Check teacher model connectivity.';\n }\n\n // Count total lines in training file\n const totalLines = readFileSync(jsonlPath, 'utf-8').split('\\n').filter(l => l.trim()).length;\n\n // Build report\n const lines = [\n `## Cloud-Assisted Training Data Generated`,\n ``,\n `| Setting | Value |`,\n `|---------|-------|`,\n `| Teacher model | ${teacherModel} |`,\n `| Examples generated | ${totalGenerated} |`,\n `| Failed | ${totalFailed} |`,\n `| Total in file | ${totalLines} |`,\n `| Mode | ${appendMode ? 'append' : 'overwrite'} |`,\n `| Output | \\`${jsonlPath}\\` |`,\n ``,\n `### By Category`,\n `| Category | Generated | Failed |`,\n `|----------|-----------|--------|`,\n ];\n\n for (const [cat, s] of Object.entries(stats)) {\n lines.push(`| ${cat} | ${s.generated} | ${s.failed} |`);\n }\n\n lines.push('');\n lines.push(`Ready to fine-tune. Use \\`train_start\\` to begin LoRA training on the local GPU.`);\n\n emitProgress({\n type: 'complete', phase: 'generate',\n message: `Cloud training data generation complete: ${totalGenerated} examples (${totalLines} total in file)`,\n detail: { examples: totalGenerated, total: totalCount },\n });\n\n return lines.join('\\n');\n}\n\n// ── Registration ─────────────────────────────────────────────────────\n\nexport function registerModelTrainerSkill(): void {\n\n registerSkill(\n {\n name: 'model_trainer',\n description: 'Use this when the user says \"train on this\", \"fine-tune with these examples\", \"add this to training data\", \"teach yourself from our conversation\", or wants to improve the local model using past interactions. Runs LoRA fine-tuning on the RTX 5090 GPU.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'train_prepare',\n description: 'Prepare training data before launching a fine-tune. Use this when asked to \"get the training data ready\", \"collect examples\", or \"prepare for training\". Scans TITAN\\'s conversation history, extracts high-quality instruction/response pairs scored by tool success rates, and saves as JSONL ready for fine-tuning.',\n parameters: {\n type: 'object',\n properties: {\n minSamples: {\n type: 'number',\n description: 'Minimum training samples required before proceeding (default: 50)',\n },\n },\n required: [],\n },\n execute: trainPrepare,\n },\n );\n\n registerSkill(\n {\n name: 'model_trainer',\n description: 'Use this when the user says \"train on this\", \"fine-tune with these examples\", \"add this to training data\", \"teach yourself from our conversation\", or wants to improve the local model using past interactions. Runs LoRA fine-tuning on the RTX 5090 GPU.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'train_start',\n description: 'Start a LoRA fine-tuning job on the local GPU. Use when the user says \"start training\", \"fine-tune the model\", or \"run the training\". Requires training data to be prepared first (train_prepare). Runs as a background process on the RTX 5090.',\n parameters: {\n type: 'object',\n properties: {\n baseModel: {\n type: 'string',\n description: 'Base model to fine-tune (default: from config, e.g. qwen3.5:35b)',\n },\n method: {\n type: 'string',\n description: 'Training method: lora, qlora, or full (default: lora)',\n },\n budgetMinutes: {\n type: 'number',\n description: 'Training time budget in minutes (default: 30)',\n },\n epochs: {\n type: 'number',\n description: 'Number of training epochs (default: 3)',\n },\n },\n required: [],\n },\n execute: trainStart,\n },\n );\n\n registerSkill(\n {\n name: 'model_trainer',\n description: 'Use this when the user says \"train on this\", \"fine-tune with these examples\", \"add this to training data\", \"teach yourself from our conversation\", or wants to improve the local model using past interactions. Runs LoRA fine-tuning on the RTX 5090 GPU.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'train_status',\n description: 'Check how a training job is progressing. Use when asked \"how is training going?\", \"what\\'s the loss?\", \"is training done?\". Shows log output, current loss, and completion status. Without a runId, lists all training runs.',\n parameters: {\n type: 'object',\n properties: {\n runId: {\n type: 'string',\n description: 'Specific training run ID to check (optional — omit to list all runs)',\n },\n },\n required: [],\n },\n execute: trainStatus,\n },\n );\n\n registerSkill(\n {\n name: 'model_trainer',\n description: 'Use this when the user says \"train on this\", \"fine-tune with these examples\", \"add this to training data\", \"teach yourself from our conversation\", or wants to improve the local model using past interactions. Runs LoRA fine-tuning on the RTX 5090 GPU.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'train_deploy',\n description: 'Deploy a completed fine-tuned model into Ollama so TITAN can use it. Use when training is done and the user says \"deploy the model\", \"use the trained model\", or \"switch to the fine-tuned version\".',\n parameters: {\n type: 'object',\n properties: {\n runId: {\n type: 'string',\n description: 'Training run ID to deploy (from train_status)',\n },\n modelName: {\n type: 'string',\n description: 'Name for the deployed Ollama model (default: titan-custom)',\n },\n },\n required: ['runId'],\n },\n execute: trainDeploy,\n },\n );\n\n // ── Cloud-Assisted Training Data Generation ──────────────────────\n\n registerSkill(\n {\n name: 'model_trainer',\n description: 'Use this when the user says \"train on this\", \"fine-tune with these examples\", \"add this to training data\", \"teach yourself from our conversation\", or wants to improve the local model using past interactions. Runs LoRA fine-tuning on the RTX 5090 GPU.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'train_generate_cloud',\n description: 'Generate synthetic training examples using a large cloud model as teacher. Use when the user says \"generate training data\", \"create examples for fine-tuning\", or \"use the cloud model to teach the local model\". A smart cloud model (e.g. qwen3.5:397b-cloud) produces diverse, high-quality agent examples across tool use, reasoning, coding, research, conversation, and error recovery.',\n parameters: {\n type: 'object',\n properties: {\n teacherModel: {\n type: 'string',\n description: 'Cloud model to use as teacher (default: qwen3.5:397b-cloud)',\n },\n count: {\n type: 'number',\n description: 'Number of training examples to generate (default: 200)',\n },\n categories: {\n type: 'string',\n description: 'Which categories to generate: tool_use, reasoning, coding, research, conversation, error_recovery (default: all)',\n },\n append: {\n type: 'boolean',\n description: 'Append to existing training data instead of overwriting (default: true)',\n },\n },\n required: [],\n },\n execute: trainGenerateCloud,\n },\n );\n\n logger.info(COMPONENT, 'Model trainer skill registered (5 tools)');\n}\n"],"mappings":"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","names":["lines"]}
|
|
@@ -2,11 +2,50 @@
|
|
|
2
2
|
import { registerSkill } from "../registry.js";
|
|
3
3
|
import { loadConfig } from "../../config/config.js";
|
|
4
4
|
import logger from "../../utils/logger.js";
|
|
5
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync, statSync } from "fs";
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync, statSync, readdirSync } from "fs";
|
|
6
6
|
import { join } from "path";
|
|
7
7
|
import { TITAN_HOME } from "../../utils/constants.js";
|
|
8
8
|
import { chat } from "../../providers/router.js";
|
|
9
9
|
const COMPONENT = "SelfImprove";
|
|
10
|
+
const SELF_IMPROVE_DIR = join(TITAN_HOME, "self-improve");
|
|
11
|
+
const LAST_MUTATION_PATH = join(SELF_IMPROVE_DIR, ".last-mutation");
|
|
12
|
+
const CHECKPOINTS_DIR = join(SELF_IMPROVE_DIR, "checkpoints");
|
|
13
|
+
function canMutate(minIntervalMs = 60 * 60 * 1e3) {
|
|
14
|
+
try {
|
|
15
|
+
if (!existsSync(LAST_MUTATION_PATH)) return true;
|
|
16
|
+
const last = parseInt(readFileSync(LAST_MUTATION_PATH, "utf-8").trim(), 10);
|
|
17
|
+
return Date.now() - last >= minIntervalMs;
|
|
18
|
+
} catch {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function recordMutation() {
|
|
23
|
+
try {
|
|
24
|
+
writeFileSync(LAST_MUTATION_PATH, String(Date.now()), "utf-8");
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function createCheckpoint(areaId, content) {
|
|
29
|
+
try {
|
|
30
|
+
mkdirSync(CHECKPOINTS_DIR, { recursive: true });
|
|
31
|
+
const checkpointId = `${areaId}-${Date.now()}`;
|
|
32
|
+
const path = join(CHECKPOINTS_DIR, `${checkpointId}.txt`);
|
|
33
|
+
writeFileSync(path, content, "utf-8");
|
|
34
|
+
return path;
|
|
35
|
+
} catch {
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function restoreCheckpoint(areaId) {
|
|
40
|
+
try {
|
|
41
|
+
const files = readdirSync(CHECKPOINTS_DIR).filter((f) => f.startsWith(areaId + "-")).sort().reverse();
|
|
42
|
+
if (files.length === 0) return null;
|
|
43
|
+
const path = join(CHECKPOINTS_DIR, files[0]);
|
|
44
|
+
return readFileSync(path, "utf-8");
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
10
49
|
let optimizedPromptCache = null;
|
|
11
50
|
function getOptimizedPromptBlock(mode) {
|
|
12
51
|
if (mode === "none") return "";
|
|
@@ -36,7 +75,6 @@ function clearOptimizedPromptCache() {
|
|
|
36
75
|
optimizedPromptCache = null;
|
|
37
76
|
logger.debug(COMPONENT, "Optimized prompt cache cleared");
|
|
38
77
|
}
|
|
39
|
-
const SELF_IMPROVE_DIR = join(TITAN_HOME, "self-improve");
|
|
40
78
|
const PROMPTS_DIR = join(SELF_IMPROVE_DIR, "prompts");
|
|
41
79
|
const BENCHMARKS_DIR = join(SELF_IMPROVE_DIR, "benchmarks");
|
|
42
80
|
const RESULTS_DIR = join(SELF_IMPROVE_DIR, "results");
|
|
@@ -379,8 +417,18 @@ Respond with only JSON:` }
|
|
|
379
417
|
session.crashes++;
|
|
380
418
|
continue;
|
|
381
419
|
}
|
|
420
|
+
if (!canMutate()) {
|
|
421
|
+
logger.info(COMPONENT, `Experiment ${i}: rate limited \u2014 skipping (min 1h between mutations)`);
|
|
422
|
+
session.crashes++;
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
const checkpointPath = createCheckpoint(area.id, currentContent);
|
|
426
|
+
if (checkpointPath) {
|
|
427
|
+
logger.debug(COMPONENT, `Checkpoint created: ${checkpointPath}`);
|
|
428
|
+
}
|
|
382
429
|
const modified = currentContent.replace(searchStr, replaceStr);
|
|
383
430
|
writeFileSync(promptPath, modified, "utf-8");
|
|
431
|
+
recordMutation();
|
|
384
432
|
const result = await runEval(area);
|
|
385
433
|
if (result.score > session.bestScore) {
|
|
386
434
|
session.bestScore = result.score;
|