Skip to content

Commit 45cace8

Browse files
committed
fix(core): add missing llmAdapter module for build
packages/core/src/index.ts exports ./llmAdapter but the file was never tracked; CI failed with 'Could not resolve ./llmAdapter'.
1 parent ec0b50b commit 45cace8

1 file changed

Lines changed: 104 additions & 0 deletions

File tree

packages/core/src/llmAdapter.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// @license Apache-2.0
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/**
5+
* Multi-LLM adapter contract for Loop Engine state-machine steps.
6+
* Provider packages (e.g. `@loop-engine/adapter-perplexity`) implement this interface.
7+
*/
8+
9+
export interface AdapterInput {
10+
prompt: string;
11+
systemPrompt?: string;
12+
model?: string;
13+
maxTokens?: number;
14+
temperature?: number;
15+
metadata?: AdapterInputMetadata;
16+
}
17+
18+
export type SearchRecencyFilter = "month" | "week" | "day" | "hour";
19+
20+
export interface AdapterInputMetadata extends Record<string, unknown> {
21+
searchDomainFilter?: string[];
22+
searchRecencyFilter?: SearchRecencyFilter;
23+
returnCitations?: boolean;
24+
returnImages?: boolean;
25+
}
26+
27+
export interface AdapterOutput {
28+
text: string;
29+
model: string;
30+
usage: {
31+
promptTokens: number;
32+
completionTokens: number;
33+
};
34+
}
35+
36+
export interface AdapterChunk {
37+
delta: string;
38+
done?: boolean;
39+
}
40+
41+
export interface LLMAdapter {
42+
name: string;
43+
invoke(input: AdapterInput): Promise<AdapterOutput>;
44+
stream?(input: AdapterInput): AsyncIterable<AdapterChunk>;
45+
guardEvidence(payload: unknown): unknown;
46+
}
47+
48+
export interface GuardEvidenceOptions {
49+
/** Object keys to omit entirely (case-insensitive match). */
50+
stripFields?: string[];
51+
/** Regexes applied to string values to mask secrets (e.g. API key patterns). */
52+
maskPatterns?: RegExp[];
53+
}
54+
55+
function normalizeKey(key: string): string {
56+
return key.toLowerCase();
57+
}
58+
59+
/**
60+
* Deep-redact payloads before logging or persisting to an audit trail.
61+
* Strips configured keys and masks matching substrings in strings.
62+
*/
63+
export function guardEvidence(payload: unknown, options?: GuardEvidenceOptions): unknown {
64+
const strip = new Set((options?.stripFields ?? []).map(normalizeKey));
65+
const masks = options?.maskPatterns ?? [];
66+
67+
const walk = (value: unknown): unknown => {
68+
if (value === null || value === undefined) {
69+
return value;
70+
}
71+
if (typeof value === "string") {
72+
let s = value;
73+
for (const pattern of masks) {
74+
s = s.replace(pattern, "[REDACTED]");
75+
}
76+
return s;
77+
}
78+
if (typeof value === "number" || typeof value === "boolean") {
79+
return value;
80+
}
81+
if (typeof value === "bigint") {
82+
return value;
83+
}
84+
if (value instanceof Date) {
85+
return value;
86+
}
87+
if (Array.isArray(value)) {
88+
return value.map((item) => walk(item));
89+
}
90+
if (typeof value === "object") {
91+
const out: Record<string, unknown> = {};
92+
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
93+
if (strip.has(normalizeKey(k))) {
94+
continue;
95+
}
96+
out[k] = walk(v);
97+
}
98+
return out;
99+
}
100+
return value;
101+
};
102+
103+
return walk(payload);
104+
}

0 commit comments

Comments
 (0)