| 158 | const LLM_BATCH_CONCURRENCY = 4; |
| 159 | |
| 160 | class LlmStrategy implements SummarizationStrategy { |
| 161 | readonly type = 'llm' as const; |
| 162 | private url: string; |
| 163 | private model: string; |
| 164 | |
| 165 | constructor(config: SummarizerConfig) { |
| 166 | this.url = |
| 167 | config.llmUrl ?? |
| 168 | `${window.location.protocol}//${window.location.hostname}:11434`; |
| 169 | this.model = config.llmModel ?? 'llama3.2'; |
| 170 | } |
| 171 | |
| 172 | async init(): Promise<void> {} |
| 173 | |
| 174 | private buildPrompt(meta: SymbolMetadata): string { |
| 175 | const source = meta.source ?? meta.name; |
| 176 | switch (meta.kind) { |
| 177 | case 'function': |
| 178 | return `Summarize what this function does in one sentence:\n\n${source}`; |
| 179 | case 'class': |
| 180 | return `Summarize what this class does in one sentence:\n\n${source}`; |
| 181 | case 'file': |
| 182 | return `Summarize what this source file does in one sentence:\n\n${source}`; |
| 183 | case 'directory': |
| 184 | return `Describe the purpose of this directory in one sentence:\n\n${source}`; |
| 185 | default: |
| 186 | return `Summarize this code in one sentence:\n\n${source}`; |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | async summarize(meta: SymbolMetadata): Promise<string> { |
| 191 | const prompt = this.buildPrompt(meta); |
| 192 | const json = await withTimeout( |
| 193 | fetch(`${this.url}/v1/chat/completions`, { |
| 194 | method: 'POST', |
| 195 | headers: { 'Content-Type': 'application/json' }, |
| 196 | body: JSON.stringify({ |
| 197 | model: this.model, |
| 198 | messages: [{ role: 'user', content: prompt }], |
| 199 | max_tokens: 100, |
| 200 | stream: false, |
| 201 | }), |
| 202 | }).then(async (r) => { |
| 203 | if (!r.ok) throw new Error(`HTTP ${r.status}`); |
| 204 | return r.json(); |
| 205 | }), |
| 206 | LLM_CALL_TIMEOUT_MS, |
| 207 | `LLM summarize(${meta.name})`, |
| 208 | ); |
| 209 | return (json.choices?.[0]?.message?.content ?? '').trim(); |
| 210 | } |
| 211 | |
| 212 | async summarizeBatch(items: SymbolMetadata[]): Promise<string[]> { |
| 213 | // Bounded concurrency: an unbounded Promise.all fires one request per |
| 214 | // symbol at once, flooding the local LLM server and triggering spurious |
| 215 | // per-call timeouts under load. A failed summary is best-effort → ''. |
| 216 | const results: string[] = new Array(items.length).fill(''); |
| 217 | let cursor = 0; |
nothing calls this directly
no outgoing calls
no test coverage detected