@ggui-ai/negotiator 0.1.0-rc.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/LICENSE +201 -0
- package/README.md +49 -0
- package/dist/contract-hash.d.ts +54 -0
- package/dist/contract-hash.d.ts.map +1 -0
- package/dist/contract-hash.js +96 -0
- package/dist/contract-validators.d.ts +171 -0
- package/dist/contract-validators.d.ts.map +1 -0
- package/dist/contract-validators.js +478 -0
- package/dist/decision-input.d.ts +48 -0
- package/dist/decision-input.d.ts.map +1 -0
- package/dist/decision-input.js +14 -0
- package/dist/decision.d.ts +54 -0
- package/dist/decision.d.ts.map +1 -0
- package/dist/decision.js +500 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/intent.d.ts +22 -0
- package/dist/intent.d.ts.map +1 -0
- package/dist/intent.js +28 -0
- package/dist/llm-caller.d.ts +70 -0
- package/dist/llm-caller.d.ts.map +1 -0
- package/dist/llm-caller.js +38 -0
- package/dist/llm-rerank.d.ts +101 -0
- package/dist/llm-rerank.d.ts.map +1 -0
- package/dist/llm-rerank.js +178 -0
- package/dist/negotiate.d.ts +141 -0
- package/dist/negotiate.d.ts.map +1 -0
- package/dist/negotiate.js +161 -0
- package/dist/normalize-schema.d.ts +22 -0
- package/dist/normalize-schema.d.ts.map +1 -0
- package/dist/normalize-schema.js +191 -0
- package/dist/pure.d.ts +30 -0
- package/dist/pure.d.ts.map +1 -0
- package/dist/pure.js +43 -0
- package/dist/rag-search.d.ts +73 -0
- package/dist/rag-search.d.ts.map +1 -0
- package/dist/rag-search.js +192 -0
- package/dist/rerank-eval/pairs.d.ts +28 -0
- package/dist/rerank-eval/pairs.d.ts.map +1 -0
- package/dist/rerank-eval/pairs.js +531 -0
- package/dist/rerank-eval/run-probe-cli.d.ts +3 -0
- package/dist/rerank-eval/run-probe-cli.d.ts.map +1 -0
- package/dist/rerank-eval/run-probe-cli.js +146 -0
- package/dist/rerank-eval/run-probe.d.ts +68 -0
- package/dist/rerank-eval/run-probe.d.ts.map +1 -0
- package/dist/rerank-eval/run-probe.js +113 -0
- package/dist/session.d.ts +42 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +21 -0
- package/dist/suggestion.d.ts +38 -0
- package/dist/suggestion.d.ts.map +1 -0
- package/dist/suggestion.js +47 -0
- package/dist/synth-bench/corpus.d.ts +106 -0
- package/dist/synth-bench/corpus.d.ts.map +1 -0
- package/dist/synth-bench/corpus.js +994 -0
- package/dist/synth-bench/run-bench-cli.d.ts +3 -0
- package/dist/synth-bench/run-bench-cli.d.ts.map +1 -0
- package/dist/synth-bench/run-bench-cli.js +181 -0
- package/dist/synth-bench/run-bench.d.ts +101 -0
- package/dist/synth-bench/run-bench.d.ts.map +1 -0
- package/dist/synth-bench/run-bench.js +374 -0
- package/dist/synthesize-contract.d.ts +131 -0
- package/dist/synthesize-contract.d.ts.map +1 -0
- package/dist/synthesize-contract.js +948 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/package.json +74 -0
- package/src/contract-hash.ts +102 -0
- package/src/contract-validators.ts +604 -0
- package/src/decision-input.ts +49 -0
- package/src/decision.ts +581 -0
- package/src/index.ts +63 -0
- package/src/intent.ts +37 -0
- package/src/llm-caller.ts +82 -0
- package/src/llm-rerank.ts +280 -0
- package/src/negotiate.ts +312 -0
- package/src/normalize-schema.ts +193 -0
- package/src/pure.ts +46 -0
- package/src/rag-search.ts +274 -0
- package/src/rerank-eval/pairs.ts +624 -0
- package/src/rerank-eval/run-probe-cli.ts +197 -0
- package/src/rerank-eval/run-probe.ts +198 -0
- package/src/session.ts +41 -0
- package/src/suggestion.ts +73 -0
- package/src/synth-bench/corpus.ts +1126 -0
- package/src/synth-bench/run-bench-cli.ts +237 -0
- package/src/synth-bench/run-bench.ts +525 -0
- package/src/synthesize-contract.ts +1161 -0
- package/src/types.ts +31 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RAG Search — embedding-similarity blueprint retrieval over the
|
|
3
|
+
* public `@ggui-ai/mcp-server-core` storage seam.
|
|
4
|
+
*
|
|
5
|
+
* Given agent `prompt` + a tenant `scope` (typically appId, or the
|
|
6
|
+
* literal `"shared"` for the global catalog), this helper:
|
|
7
|
+
*
|
|
8
|
+
* 1. Embeds the prompt via {@link EmbeddingProvider.embed}.
|
|
9
|
+
* 2. Queries the {@link VectorStore} for the top-K nearest neighbors
|
|
10
|
+
* (server-side cosine, scope-partitioned — no cross-tenant leak).
|
|
11
|
+
* 3. Filters by minimum score, biases private (per-app registered
|
|
12
|
+
* UIs) over shared (generated pool) at the same score band, and
|
|
13
|
+
* projects each hit into a {@link NegotiatorOption}.
|
|
14
|
+
*
|
|
15
|
+
* Two pipeline paths emerge from the confidence band:
|
|
16
|
+
* - **High confidence** (`score >= HIGH_CONFIDENCE_THRESHOLD`): the
|
|
17
|
+
* hit is marked `exact`. Upstream callers (V3 `negotiate()`) can
|
|
18
|
+
* short-circuit the decision LLM entirely.
|
|
19
|
+
* - **Medium confidence** (`score >= RETRIEVAL_MIN_SCORE` and below
|
|
20
|
+
* the exact threshold): marked `partial`. Upstream passes these to
|
|
21
|
+
* the decision LLM as candidates.
|
|
22
|
+
*
|
|
23
|
+
* ### Public-seam contract
|
|
24
|
+
*
|
|
25
|
+
* This function reads only the public `VectorStore` surface (scalar
|
|
26
|
+
* metadata). Consumers that still hold the rich `EmbeddingStorage`
|
|
27
|
+
* shape bridge via
|
|
28
|
+
* `@ggui-cloud/aws-adapters.embeddingStorageToVectorStore`, which
|
|
29
|
+
* encodes array-valued fields (`props`, `callbacks`, `sourceTools`)
|
|
30
|
+
* as JSON strings inside `metadata`. Any `VectorStore` implementation
|
|
31
|
+
* that stores fresh writes through `writeRagVector` (see
|
|
32
|
+
* `mcp-servers/ggui-protocol/src/adapters/vector-store.ts`) uses the
|
|
33
|
+
* same encoding — so existing caches, fresh writes, and community
|
|
34
|
+
* `VectorStore` backends all collide on the same retrieval key.
|
|
35
|
+
*
|
|
36
|
+
* ### Scope
|
|
37
|
+
*
|
|
38
|
+
* No AWS bindings, no LLM dependency. Pure composition over the two
|
|
39
|
+
* public seams. The one-LLM-call `makeDecision()` step is separate.
|
|
40
|
+
*/
|
|
41
|
+
import { inferJsonSchemaType } from './pure.js';
|
|
42
|
+
/** Minimum cosine similarity to be considered a retrieval candidate. */
|
|
43
|
+
const RETRIEVAL_MIN_SCORE = 0.15;
|
|
44
|
+
/** Cosine similarity above this → exact match (skip LLM). */
|
|
45
|
+
const HIGH_CONFIDENCE_THRESHOLD = 0.45;
|
|
46
|
+
/** Default top-K for the k-NN query. */
|
|
47
|
+
const DEFAULT_MAX_CANDIDATES = 10;
|
|
48
|
+
/**
|
|
49
|
+
* Search the vector index for blueprints matching `prompt` within
|
|
50
|
+
* `scope`. See the module docstring for the confidence-band pipeline
|
|
51
|
+
* and encoding contract.
|
|
52
|
+
*/
|
|
53
|
+
export async function ragSearch(deps, input) {
|
|
54
|
+
const topK = input.maxCandidates ?? DEFAULT_MAX_CANDIDATES;
|
|
55
|
+
// Stage 1: embed the query text
|
|
56
|
+
const embedStart = Date.now();
|
|
57
|
+
const queryEmbedding = await deps.embedding.embed(input.prompt);
|
|
58
|
+
const embeddingLatencyMs = Date.now() - embedStart;
|
|
59
|
+
// Stage 2: nearest-neighbor query
|
|
60
|
+
const searchStart = Date.now();
|
|
61
|
+
const results = await deps.vectors.query(input.scope, queryEmbedding, topK);
|
|
62
|
+
const searchLatencyMs = Date.now() - searchStart;
|
|
63
|
+
// Filter + sort: private (registered UIs) always beats shared (generated)
|
|
64
|
+
// at the same score level; then descending score.
|
|
65
|
+
const candidates = results
|
|
66
|
+
.filter((r) => r.score >= RETRIEVAL_MIN_SCORE)
|
|
67
|
+
.sort((a, b) => {
|
|
68
|
+
const aPrivate = readPoolSource(a.metadata) === 'private' ? 1 : 0;
|
|
69
|
+
const bPrivate = readPoolSource(b.metadata) === 'private' ? 1 : 0;
|
|
70
|
+
if (aPrivate !== bPrivate)
|
|
71
|
+
return bPrivate - aPrivate;
|
|
72
|
+
return b.score - a.score;
|
|
73
|
+
});
|
|
74
|
+
if (candidates.length === 0) {
|
|
75
|
+
return { options: [], embeddingLatencyMs, searchLatencyMs };
|
|
76
|
+
}
|
|
77
|
+
const seenContracts = new Set();
|
|
78
|
+
const options = [];
|
|
79
|
+
for (const match of candidates) {
|
|
80
|
+
const option = buildOption(match);
|
|
81
|
+
if (!option)
|
|
82
|
+
continue;
|
|
83
|
+
if (seenContracts.has(option.contractKey))
|
|
84
|
+
continue;
|
|
85
|
+
seenContracts.add(option.contractKey);
|
|
86
|
+
options.push(option.value);
|
|
87
|
+
}
|
|
88
|
+
return { options, embeddingLatencyMs, searchLatencyMs };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Project a public {@link VectorSearchResult} hit into a
|
|
92
|
+
* {@link NegotiatorOption}, together with the dedup key used to
|
|
93
|
+
* collapse hits that describe the same semantic contract.
|
|
94
|
+
*/
|
|
95
|
+
function buildOption(match) {
|
|
96
|
+
const isExact = match.score >= HIGH_CONFIDENCE_THRESHOLD;
|
|
97
|
+
const verdict = isExact ? 'exact' : 'partial';
|
|
98
|
+
const blueprintHash = match.key;
|
|
99
|
+
// Registered (private) blueprints already carry a `p_` prefix in
|
|
100
|
+
// their hash; generated pool entries do not — prefix them as `c_`
|
|
101
|
+
// to match the legacy option ID format that downstream consumers
|
|
102
|
+
// (decision prompt, cache layer) have been reading since V2.
|
|
103
|
+
const blueprintId = blueprintHash.startsWith('p_')
|
|
104
|
+
? blueprintHash
|
|
105
|
+
: `c_${blueprintHash}`;
|
|
106
|
+
const prompt = readString(match.metadata.prompt, '');
|
|
107
|
+
const intent = readString(match.metadata.intent, '');
|
|
108
|
+
const category = readString(match.metadata.category, '');
|
|
109
|
+
const contractHash = readString(match.metadata.contractHash, '');
|
|
110
|
+
const poolSource = readPoolSource(match.metadata);
|
|
111
|
+
const featured = Boolean(match.metadata.featured);
|
|
112
|
+
const props = readProps(match.metadata.props);
|
|
113
|
+
const contract = buildContract({ prompt, intent, category, props });
|
|
114
|
+
// Dedup: prefer matching on semantic intent; fall back to prop
|
|
115
|
+
// signature so two blueprints with identical intents but different
|
|
116
|
+
// prop shapes don't collapse together.
|
|
117
|
+
const contractKey = intent !== ''
|
|
118
|
+
? intent
|
|
119
|
+
: JSON.stringify(Object.keys(contract.propsSpec?.properties ?? {}).sort());
|
|
120
|
+
const option = {
|
|
121
|
+
id: `rag_${blueprintId.slice(-8)}`,
|
|
122
|
+
type: 'blueprint',
|
|
123
|
+
blueprintId,
|
|
124
|
+
pattern: category,
|
|
125
|
+
description: `${prompt} (similarity: ${Math.round(match.score * 100)}%, ${verdict})`,
|
|
126
|
+
pros: [
|
|
127
|
+
isExact
|
|
128
|
+
? 'Exact blueprint match — instant render'
|
|
129
|
+
: 'Semantically matched to your request',
|
|
130
|
+
...(featured ? ['Featured blueprint — curated quality'] : []),
|
|
131
|
+
],
|
|
132
|
+
cons: [
|
|
133
|
+
...(isExact ? [] : ['Partial match — may need adjustments']),
|
|
134
|
+
...(!isExact ? ['Fixed layout — limited customization'] : []),
|
|
135
|
+
],
|
|
136
|
+
renderTime: 'instant',
|
|
137
|
+
contract,
|
|
138
|
+
...(contractHash !== '' ? { contractHash } : {}),
|
|
139
|
+
...(poolSource !== undefined ? { poolSource } : {}),
|
|
140
|
+
};
|
|
141
|
+
return { value: option, contractKey };
|
|
142
|
+
}
|
|
143
|
+
function buildContract(args) {
|
|
144
|
+
// `intent` is not a contract field. The `prompt` and `intent` fields
|
|
145
|
+
// stay on the args because callers still use them for RAG keys +
|
|
146
|
+
// dedup; the returned contract carries structural shape only.
|
|
147
|
+
const { props } = args;
|
|
148
|
+
return {
|
|
149
|
+
propsSpec: {
|
|
150
|
+
properties: Object.fromEntries(props.map((p) => [
|
|
151
|
+
p.name,
|
|
152
|
+
{
|
|
153
|
+
description: p.description,
|
|
154
|
+
schema: { type: inferJsonSchemaType(p.type) },
|
|
155
|
+
required: p.required,
|
|
156
|
+
...(p.example !== undefined
|
|
157
|
+
? { example: p.example }
|
|
158
|
+
: {}),
|
|
159
|
+
},
|
|
160
|
+
])),
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function readString(value, fallback) {
|
|
165
|
+
return typeof value === 'string' ? value : fallback;
|
|
166
|
+
}
|
|
167
|
+
function readPoolSource(metadata) {
|
|
168
|
+
const v = metadata.poolSource;
|
|
169
|
+
return v === 'shared' || v === 'private' ? v : undefined;
|
|
170
|
+
}
|
|
171
|
+
function readProps(value) {
|
|
172
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
173
|
+
return [];
|
|
174
|
+
try {
|
|
175
|
+
const parsed = JSON.parse(value);
|
|
176
|
+
if (!Array.isArray(parsed))
|
|
177
|
+
return [];
|
|
178
|
+
return parsed.filter(isPropSpec);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function isPropSpec(value) {
|
|
185
|
+
if (value === null || typeof value !== 'object')
|
|
186
|
+
return false;
|
|
187
|
+
const v = value;
|
|
188
|
+
return (typeof v.name === 'string' &&
|
|
189
|
+
typeof v.type === 'string' &&
|
|
190
|
+
typeof v.required === 'boolean' &&
|
|
191
|
+
typeof v.description === 'string');
|
|
192
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Eval pairs for the rerank quality probe.
|
|
3
|
+
*
|
|
4
|
+
* 25 hand-built pairs. Each pair has:
|
|
5
|
+
* - A user query (intent + contract summary)
|
|
6
|
+
* - 3-5 candidates (one or zero of which is the gold match)
|
|
7
|
+
* - The gold-standard label: `goldMatchId` or `null` for no-match
|
|
8
|
+
* - `kind`: 'should-match' | 'no-match' | 'adversarial'
|
|
9
|
+
*
|
|
10
|
+
* Adversarial pairs (n=10) are structurally identical to a candidate
|
|
11
|
+
* but differ in load-bearing intent — the judge MUST reject them. If
|
|
12
|
+
* the judge accepts adversarials, the adversarial false-positive rate
|
|
13
|
+
* exceeds its quality gate.
|
|
14
|
+
*
|
|
15
|
+
* Eval-only — not exported from the package index.
|
|
16
|
+
*/
|
|
17
|
+
import type { RerankCandidate, RerankQuery } from '../llm-rerank.js';
|
|
18
|
+
export interface EvalPair {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly kind: 'should-match' | 'no-match' | 'adversarial';
|
|
21
|
+
readonly query: RerankQuery;
|
|
22
|
+
readonly candidates: readonly RerankCandidate[];
|
|
23
|
+
readonly goldMatchId: string | null;
|
|
24
|
+
readonly note?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare const EVAL_PAIRS: readonly EvalPair[];
|
|
27
|
+
export declare function pairsByKind(kind: EvalPair['kind']): readonly EvalPair[];
|
|
28
|
+
//# sourceMappingURL=pairs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairs.d.ts","sourceRoot":"","sources":["../../src/rerank-eval/pairs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAErE,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,GAAG,aAAa,CAAC;IAC3D,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC;IAChD,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAglBzC,CAAC;AAEF,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS,QAAQ,EAAE,CAEvE"}
|