Skip to content

Commit 20d980a

Browse files
author
StackMemory Bot (CLI)
committed
feat(provenant): support keyword-only query without ANTHROPIC_API_KEY
When ANTHROPIC_API_KEY is not set, provenant query now returns raw context from keyword search instead of failing. Prints a note about running in keyword-only mode.
1 parent 0fe96ec commit 20d980a

2 files changed

Lines changed: 20 additions & 14 deletions

File tree

packages/provenant/src/cli/commands/query.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ export async function runQuery(
2323

2424
const apiKey = process.env['ANTHROPIC_API_KEY'];
2525
if (!apiKey) {
26-
console.error('ANTHROPIC_API_KEY not set. Required for query.');
27-
process.exit(1);
26+
console.log(
27+
'Running in keyword-only mode (set ANTHROPIC_API_KEY for AI answers)'
28+
);
2829
}
2930

3031
const db = new Database(opts.db);

packages/provenant/src/query/engine.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export interface Citation {
2626
}
2727

2828
export interface QueryConfig {
29-
anthropicApiKey: string;
29+
anthropicApiKey?: string; // omit for keyword-only mode (no LLM)
3030
model?: string; // default: claude-sonnet-4-6
3131
maxNodes?: number; // max nodes to include in context (default 20)
3232
actorFilter?: string;
@@ -80,24 +80,29 @@ export async function query(
8080
const uniqueContradictions = dedup(contradictions, (c) => c.id);
8181
const unresolvedRejections = db.getUnresolvedRejections().length;
8282

83-
// Step 3: Build context and ask Claude
83+
// Step 3: Build context and optionally ask Claude
8484
const context = buildContext(
8585
citations,
8686
uniqueStale,
8787
uniqueContradictions,
8888
unresolvedRejections
8989
);
9090
let answer: string;
91-
try {
92-
answer = await askClaude(question, context, config);
93-
} catch (err) {
94-
// LLM unavailable — return raw context as the answer
95-
const msg = err instanceof Error ? err.message : 'unknown error';
96-
// Sanitize error message to avoid leaking API keys in auth errors
97-
const safeMsg = msg
98-
.replace(/sk-[a-zA-Z0-9-_]+/g, '[REDACTED]')
99-
.replace(/key[_-]?[a-zA-Z0-9]{16,}/gi, '[REDACTED]');
100-
answer = `[Claude unavailable: ${safeMsg}]\n\nRaw context:\n${context}`;
91+
if (!config.anthropicApiKey) {
92+
// Keyword-only mode — return raw context without LLM summarization
93+
answer = context;
94+
} else {
95+
try {
96+
answer = await askClaude(question, context, config);
97+
} catch (err) {
98+
// LLM unavailable — return raw context as the answer
99+
const msg = err instanceof Error ? err.message : 'unknown error';
100+
// Sanitize error message to avoid leaking API keys in auth errors
101+
const safeMsg = msg
102+
.replace(/sk-[a-zA-Z0-9-_]+/g, '[REDACTED]')
103+
.replace(/key[_-]?[a-zA-Z0-9]{16,}/gi, '[REDACTED]');
104+
answer = `[Claude unavailable: ${safeMsg}]\n\nRaw context:\n${context}`;
105+
}
101106
}
102107

103108
return {

0 commit comments

Comments
 (0)