✨ Major Features: - Native session management without OpenCode binary - Provider routing: OpenCode Zen (free), Qwen OAuth, Z.AI - Streaming chat with tool execution loop - Mode detection API (/api/meta/mode) - MCP integration fix (resolved infinite loading) - NomadArch Native option in UI with comparison info 🆓 Free Models (No API Key): - GPT-5 Nano (400K context) - Grok Code Fast 1 (256K context) - GLM-4.7 (205K context) - Doubao Seed Code (256K context) - Big Pickle (200K context) 📦 New Files: - session-store.ts: Native session persistence - native-sessions.ts: REST API for sessions - lite-mode.ts: UI mode detection client - native-sessions.ts (UI): SolidJS store 🔧 Updated: - All installers: Optional binary download - All launchers: Mode detection display - Binary selector: Added NomadArch Native option - README: Binary-Free Mode documentation
202 lines
5.5 KiB
TypeScript
202 lines
5.5 KiB
TypeScript
/**
|
|
* Context Engine Service
|
|
*
|
|
* Source: https://github.com/Eskapeum/Context-Engine
|
|
*
|
|
* Provides intelligent context retrieval for enhanced AI responses:
|
|
* - File indexing and caching
|
|
* - Semantic search across codebase
|
|
* - Q&A memory for persistent knowledge
|
|
*/
|
|
|
|
import { getLogger } from "@/lib/logger";
|
|
|
|
const log = getLogger("context-engine");
|
|
|
|
export interface ContextEngineConfig {
|
|
projectRoot: string;
|
|
enableIndexing?: boolean;
|
|
enableVectors?: boolean;
|
|
}
|
|
|
|
export interface RetrievedContext {
|
|
content: string;
|
|
sources: ContextSource[];
|
|
relevanceScore: number;
|
|
}
|
|
|
|
export interface ContextSource {
|
|
file: string;
|
|
line?: number;
|
|
symbol?: string;
|
|
type: "code" | "documentation" | "memory" | "qa";
|
|
}
|
|
|
|
export interface IndexStats {
|
|
filesIndexed: number;
|
|
symbolsFound: number;
|
|
lastUpdated: number;
|
|
}
|
|
|
|
// Singleton instance
|
|
let instance: ContextEngineService | null = null;
|
|
|
|
export class ContextEngineService {
|
|
private config: ContextEngineConfig;
|
|
private initialized: boolean = false;
|
|
private stats: IndexStats = { filesIndexed: 0, symbolsFound: 0, lastUpdated: 0 };
|
|
private memoryCache: Map<string, { question: string; answer: string; timestamp: number }> = new Map();
|
|
|
|
constructor(config: ContextEngineConfig) {
|
|
this.config = config;
|
|
}
|
|
|
|
/**
|
|
* Initialize the context engine
|
|
*/
|
|
async initialize(): Promise<void> {
|
|
if (this.initialized) return;
|
|
|
|
log.info("Context Engine initializing...", { projectRoot: this.config.projectRoot });
|
|
|
|
// In a full implementation, this would:
|
|
// 1. Scan the project directory
|
|
// 2. Build Tree-sitter AST for each file
|
|
// 3. Create embeddings for semantic search
|
|
|
|
this.initialized = true;
|
|
log.info("Context Engine initialized");
|
|
}
|
|
|
|
/**
|
|
* Retrieve relevant context for a query
|
|
*/
|
|
async retrieve(query: string, options?: { maxTokens?: number }): Promise<RetrievedContext> {
|
|
if (!this.initialized) {
|
|
await this.initialize();
|
|
}
|
|
|
|
log.info("Retrieving context for query", { query: query.substring(0, 50) });
|
|
|
|
// Search memory cache first
|
|
const memorySuggestions = this.searchMemory(query);
|
|
|
|
// In a full implementation, this would:
|
|
// 1. Vectorize the query
|
|
// 2. Search the index for relevant files/symbols
|
|
// 3. Rank results by relevance
|
|
// 4. Return top matches within token budget
|
|
|
|
return {
|
|
content: memorySuggestions.join("\n\n"),
|
|
sources: [],
|
|
relevanceScore: 0
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Index or re-index the project
|
|
*/
|
|
async index(options?: { force?: boolean }): Promise<IndexStats> {
|
|
log.info("Indexing project...", { force: options?.force });
|
|
|
|
// In a full implementation, this would:
|
|
// 1. Walk the file tree
|
|
// 2. Parse each file with Tree-sitter
|
|
// 3. Extract symbols and documentation
|
|
// 4. Generate embeddings
|
|
|
|
this.stats = {
|
|
filesIndexed: 0,
|
|
symbolsFound: 0,
|
|
lastUpdated: Date.now()
|
|
};
|
|
|
|
return this.stats;
|
|
}
|
|
|
|
/**
|
|
* Get current index stats
|
|
*/
|
|
getStats(): IndexStats {
|
|
return this.stats;
|
|
}
|
|
|
|
/**
|
|
* Add to Q&A memory
|
|
*/
|
|
async remember(question: string, answer: string): Promise<void> {
|
|
const id = `qa_${Date.now()}`;
|
|
this.memoryCache.set(id, {
|
|
question,
|
|
answer,
|
|
timestamp: Date.now()
|
|
});
|
|
log.info("Remembered Q&A", { question: question.substring(0, 50) });
|
|
}
|
|
|
|
/**
|
|
* Search Q&A memory
|
|
*/
|
|
searchMemory(query: string): string[] {
|
|
const results: string[] = [];
|
|
const queryLower = query.toLowerCase();
|
|
|
|
for (const [, entry] of this.memoryCache) {
|
|
if (entry.question.toLowerCase().includes(queryLower) ||
|
|
entry.answer.toLowerCase().includes(queryLower)) {
|
|
results.push(`Q: ${entry.question}\nA: ${entry.answer}`);
|
|
}
|
|
}
|
|
|
|
return results.slice(0, 5);
|
|
}
|
|
|
|
/**
|
|
* Search Q&A memory (async version)
|
|
*/
|
|
async recall(query: string): Promise<{ question: string; answer: string }[]> {
|
|
log.info("Recalling from memory", { query: query.substring(0, 50) });
|
|
|
|
const results: { question: string; answer: string }[] = [];
|
|
const queryLower = query.toLowerCase();
|
|
|
|
for (const [, entry] of this.memoryCache) {
|
|
if (entry.question.toLowerCase().includes(queryLower) ||
|
|
entry.answer.toLowerCase().includes(queryLower)) {
|
|
results.push({ question: entry.question, answer: entry.answer });
|
|
}
|
|
}
|
|
|
|
return results.slice(0, 10);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get or create context engine instance
|
|
*/
|
|
export function getContextEngine(config?: ContextEngineConfig): ContextEngineService {
|
|
if (!instance && config) {
|
|
instance = new ContextEngineService(config);
|
|
}
|
|
if (!instance) {
|
|
throw new Error("Context engine not initialized. Provide config on first call.");
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
/**
|
|
* Initialize context engine for a workspace
|
|
*/
|
|
export async function initializeContextEngine(projectRoot: string): Promise<ContextEngineService> {
|
|
const service = getContextEngine({ projectRoot });
|
|
await service.initialize();
|
|
return service;
|
|
}
|
|
|
|
export default {
|
|
ContextEngineService,
|
|
getContextEngine,
|
|
initializeContextEngine,
|
|
};
|