@michaelborck/honeyguide-core 0.6.0 → 0.7.0
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/README.md +4 -0
- package/dist/align/outcomes.d.ts +75 -0
- package/dist/align/outcomes.js +266 -0
- package/dist/align/outcomes.js.map +1 -0
- package/dist/align/reasons.d.ts +11 -0
- package/dist/align/reasons.js +84 -0
- package/dist/align/reasons.js.map +1 -0
- package/dist/annotate/ladder.d.ts +1 -1
- package/dist/annotate/ladder.js +2 -2
- package/dist/annotate/ladder.js.map +1 -1
- package/dist/annotate/ollama.d.ts +3 -1
- package/dist/annotate/ollama.js +17 -11
- package/dist/annotate/ollama.js.map +1 -1
- package/dist/batch.d.ts +0 -0
- package/dist/batch.js +0 -0
- package/dist/batch.js.map +0 -0
- package/dist/brief.d.ts +30 -0
- package/dist/brief.js +70 -0
- package/dist/brief.js.map +1 -0
- package/dist/collection.d.ts +27 -0
- package/dist/collection.js +98 -0
- package/dist/collection.js.map +1 -0
- package/dist/config.d.ts +31 -1
- package/dist/config.js +10 -0
- package/dist/config.js.map +1 -1
- package/dist/enrich/page.d.ts +0 -0
- package/dist/enrich/page.js +0 -0
- package/dist/enrich/page.js.map +0 -0
- package/dist/enrich/sourceType.d.ts +0 -0
- package/dist/enrich/sourceType.js +0 -0
- package/dist/enrich/sourceType.js.map +0 -0
- package/dist/enrich/unpaywall.d.ts +28 -0
- package/dist/enrich/unpaywall.js +36 -0
- package/dist/enrich/unpaywall.js.map +1 -0
- package/dist/export/citations.d.ts +0 -0
- package/dist/export/citations.js +0 -0
- package/dist/export/citations.js.map +0 -0
- package/dist/export/lmsHtml.d.ts +0 -0
- package/dist/export/lmsHtml.js +16 -2
- package/dist/export/lmsHtml.js.map +1 -1
- package/dist/export/markdown.d.ts +0 -0
- package/dist/export/markdown.js +11 -3
- package/dist/export/markdown.js.map +1 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline.d.ts +2 -2
- package/dist/pipeline.js +117 -12
- package/dist/pipeline.js.map +1 -1
- package/dist/rank/decision.d.ts +59 -0
- package/dist/rank/decision.js +124 -0
- package/dist/rank/decision.js.map +1 -0
- package/dist/rank/diversity.d.ts +0 -0
- package/dist/rank/diversity.js +0 -0
- package/dist/rank/diversity.js.map +0 -0
- package/dist/rank/embeddings.d.ts +41 -0
- package/dist/rank/embeddings.js +98 -0
- package/dist/rank/embeddings.js.map +1 -0
- package/dist/rank/reranker.d.ts +21 -0
- package/dist/rank/reranker.js +30 -0
- package/dist/rank/reranker.js.map +1 -0
- package/dist/rank/semantic.d.ts +14 -0
- package/dist/rank/semantic.js +58 -0
- package/dist/rank/semantic.js.map +1 -0
- package/dist/recheck.d.ts +0 -0
- package/dist/recheck.js +0 -0
- package/dist/recheck.js.map +0 -0
- package/dist/retrieval/arxiv.d.ts +24 -0
- package/dist/retrieval/arxiv.js +94 -0
- package/dist/retrieval/arxiv.js.map +1 -0
- package/dist/retrieval/books.d.ts +0 -0
- package/dist/retrieval/books.js +0 -0
- package/dist/retrieval/books.js.map +0 -0
- package/dist/retrieval/breaker.d.ts +16 -0
- package/dist/retrieval/breaker.js +42 -0
- package/dist/retrieval/breaker.js.map +1 -0
- package/dist/retrieval/cache.d.ts +8 -0
- package/dist/retrieval/cache.js +33 -0
- package/dist/retrieval/cache.js.map +1 -0
- package/dist/retrieval/candidates.d.ts +1 -1
- package/dist/retrieval/candidates.js +31 -7
- package/dist/retrieval/candidates.js.map +1 -1
- package/dist/retrieval/counterpoint.d.ts +0 -0
- package/dist/retrieval/counterpoint.js +1 -0
- package/dist/retrieval/counterpoint.js.map +1 -1
- package/dist/retrieval/europePmc.d.ts +32 -0
- package/dist/retrieval/europePmc.js +62 -0
- package/dist/retrieval/europePmc.js.map +1 -0
- package/dist/retrieval/openalexTopical.d.ts +0 -0
- package/dist/retrieval/openalexTopical.js +0 -0
- package/dist/retrieval/openalexTopical.js.map +0 -0
- package/dist/retrieval/paperMerge.d.ts +16 -0
- package/dist/retrieval/paperMerge.js +77 -0
- package/dist/retrieval/paperMerge.js.map +1 -0
- package/dist/retrieval/papers.d.ts +10 -5
- package/dist/retrieval/papers.js +51 -32
- package/dist/retrieval/papers.js.map +1 -1
- package/dist/retrieval/podcasts.d.ts +0 -0
- package/dist/retrieval/podcasts.js +0 -0
- package/dist/retrieval/podcasts.js.map +0 -0
- package/dist/retrieval/query.d.ts +0 -0
- package/dist/retrieval/query.js +1 -0
- package/dist/retrieval/query.js.map +1 -1
- package/dist/retrieval/rateLimit.d.ts +6 -0
- package/dist/retrieval/rateLimit.js +15 -0
- package/dist/retrieval/rateLimit.js.map +1 -0
- package/dist/retrieval/search/brave.d.ts +13 -0
- package/dist/retrieval/search/brave.js +32 -0
- package/dist/retrieval/search/brave.js.map +1 -0
- package/dist/retrieval/search/index.d.ts +13 -0
- package/dist/retrieval/search/index.js +22 -0
- package/dist/retrieval/search/index.js.map +1 -0
- package/dist/retrieval/search/searxng.d.ts +14 -0
- package/dist/retrieval/search/searxng.js +24 -0
- package/dist/retrieval/search/searxng.js.map +1 -0
- package/dist/retrieval/search/serper.d.ts +14 -0
- package/dist/retrieval/search/serper.js +38 -0
- package/dist/retrieval/search/serper.js.map +1 -0
- package/dist/retrieval/search/types.d.ts +17 -0
- package/dist/retrieval/search/types.js +2 -0
- package/dist/retrieval/search/types.js.map +1 -0
- package/dist/retrieval/videos.d.ts +0 -0
- package/dist/retrieval/videos.js +0 -0
- package/dist/retrieval/videos.js.map +0 -0
- package/dist/retrieval/web.d.ts +5 -6
- package/dist/retrieval/web.js +14 -27
- package/dist/retrieval/web.js.map +1 -1
- package/dist/types.d.ts +62 -0
- package/dist/types.js +0 -0
- package/dist/types.js.map +0 -0
- package/dist/verify/verify.d.ts +11 -2
- package/dist/verify/verify.js +13 -4
- package/dist/verify/verify.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,3 +8,7 @@ Shared logic for Honeyguide: retrieval, verification, ranking and rationale.
|
|
|
8
8
|
([ADR-0002](../../docs/adr/0002-extend-cite-sight-core-for-verification.md)).
|
|
9
9
|
- Every enrichment step degrades non-fatally
|
|
10
10
|
([ADR-0006](../../docs/adr/0006-non-fatal-degradation.md)).
|
|
11
|
+
- Paper discovery is multi-source — OpenAlex, Semantic Scholar, Europe PMC and
|
|
12
|
+
arXiv, merged and deduplicated, with Crossref as the fallback. Web search
|
|
13
|
+
goes through a pluggable provider (SearXNG, Brave or Serper)
|
|
14
|
+
([ADR-0013](../../docs/adr/0013-multi-source-paper-retrieval-and-search-providers.md)).
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Alignment, Outcome, Resource } from '../types.js';
|
|
2
|
+
import type { PipelineConfig } from '../config.js';
|
|
3
|
+
import { type EmbeddingProvider } from '../rank/embeddings.js';
|
|
4
|
+
export declare const MAX_ALIGN_RESOURCES = 12;
|
|
5
|
+
export declare const MAX_ALIGN_OUTCOMES = 8;
|
|
6
|
+
/**
|
|
7
|
+
* Extract outcomes from a plain list or a pasted unit outline. When an
|
|
8
|
+
* "Outcomes" heading is present, only that section is used; otherwise every
|
|
9
|
+
* non-structural line is a candidate. List markers, numbering and outcome
|
|
10
|
+
* codes (LO1, CLO2…) are stripped.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseOutcomes(text: string): Outcome[];
|
|
13
|
+
export interface AlignmentQuestion {
|
|
14
|
+
type: 'choice';
|
|
15
|
+
instructions: string;
|
|
16
|
+
criteria: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export interface AlignmentRequest {
|
|
19
|
+
model: string;
|
|
20
|
+
state: {
|
|
21
|
+
topic: string;
|
|
22
|
+
outcomes: Array<{
|
|
23
|
+
id: string;
|
|
24
|
+
text: string;
|
|
25
|
+
}>;
|
|
26
|
+
resources: Array<{
|
|
27
|
+
id: string;
|
|
28
|
+
title: string;
|
|
29
|
+
venue?: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
questions: Record<string, AlignmentQuestion>;
|
|
34
|
+
keep_alive: string;
|
|
35
|
+
}
|
|
36
|
+
export interface BuiltAlignmentRequest {
|
|
37
|
+
request: AlignmentRequest;
|
|
38
|
+
resourceById: Map<string, Resource>;
|
|
39
|
+
outcomeById: Map<string, Outcome>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build one `/v1/systemone` request: a Choice per resource over the outcome
|
|
43
|
+
* list plus `none`. Choice probabilities let us keep near-ties instead of
|
|
44
|
+
* blindly trusting a `none` that won by a rounding error.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildAlignmentRequest(topic: string, resources: Resource[], outcomes: Outcome[], model: string): BuiltAlignmentRequest;
|
|
47
|
+
export declare function parseAlignmentResponse(response: unknown, resourceById: Map<string, Resource>, outcomeById: Map<string, Outcome>): Map<Resource, Alignment>;
|
|
48
|
+
export interface OutcomeAligner {
|
|
49
|
+
readonly name: 'decision' | 'embeddings';
|
|
50
|
+
align(topic: string, resources: Resource[], outcomes: Outcome[]): Promise<Map<Resource, Alignment>>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Local System One alignment (ADR-0017): one Choice per resource. Preferred
|
|
54
|
+
* because it reasons about fit rather than surface similarity.
|
|
55
|
+
*/
|
|
56
|
+
export declare class OllamaOutcomeAligner implements OutcomeAligner {
|
|
57
|
+
private readonly baseUrl;
|
|
58
|
+
private readonly model;
|
|
59
|
+
private readonly apiKey?;
|
|
60
|
+
readonly name = "decision";
|
|
61
|
+
constructor(baseUrl: string, model?: string, apiKey?: string | undefined);
|
|
62
|
+
align(topic: string, resources: Resource[], outcomes: Outcome[]): Promise<Map<Resource, Alignment>>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Embedding fallback: cosine similarity between each resource and outcome.
|
|
66
|
+
* Less precise than a Choice, but works anywhere embeddings do.
|
|
67
|
+
*/
|
|
68
|
+
export declare class EmbeddingOutcomeAligner implements OutcomeAligner {
|
|
69
|
+
private readonly provider;
|
|
70
|
+
readonly name = "embeddings";
|
|
71
|
+
constructor(provider: EmbeddingProvider);
|
|
72
|
+
align(_topic: string, resources: Resource[], outcomes: Outcome[]): Promise<Map<Resource, Alignment>>;
|
|
73
|
+
}
|
|
74
|
+
/** Pick the configured aligner: decision model first, embeddings second. */
|
|
75
|
+
export declare function createOutcomeAligner(config: PipelineConfig): OutcomeAligner | null;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { createEmbeddingProvider } from '../rank/embeddings.js';
|
|
2
|
+
import { cosineSimilarity } from '../rank/semantic.js';
|
|
3
|
+
const TIMEOUT_MS = 30_000;
|
|
4
|
+
export const MAX_ALIGN_RESOURCES = 12;
|
|
5
|
+
export const MAX_ALIGN_OUTCOMES = 8;
|
|
6
|
+
const MAX_TEXT_CHARS = 240;
|
|
7
|
+
const EMBEDDING_MIN_SCORE = 0.35;
|
|
8
|
+
const OUTCOMES_HEADING = /^(?:(?:learning|intended|course|unit|subject|module|program|programme|graduate)\s+)*outcomes?$|^(?:ilos?|clos?|ulos?|slos?)$/i;
|
|
9
|
+
const OTHER_HEADING = /^(?:week|module|topic|unit|lecture|seminar|workshop|assessment|assessments|resources|readings?|activities|schedule|content|description|overview|introduction)\b/i;
|
|
10
|
+
/**
|
|
11
|
+
* Extract outcomes from a plain list or a pasted unit outline. When an
|
|
12
|
+
* "Outcomes" heading is present, only that section is used; otherwise every
|
|
13
|
+
* non-structural line is a candidate. List markers, numbering and outcome
|
|
14
|
+
* codes (LO1, CLO2…) are stripped.
|
|
15
|
+
*/
|
|
16
|
+
export function parseOutcomes(text) {
|
|
17
|
+
const lines = text.split(/\r?\n/);
|
|
18
|
+
let start = 0;
|
|
19
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
20
|
+
if (OUTCOMES_HEADING.test(heading(lines[i]))) {
|
|
21
|
+
start = i + 1;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const hasOutcomesHeading = start > 0;
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
const texts = [];
|
|
28
|
+
for (let i = start; i < lines.length; i++) {
|
|
29
|
+
const line = lines[i].trim();
|
|
30
|
+
if (!line)
|
|
31
|
+
continue;
|
|
32
|
+
const clean = heading(line);
|
|
33
|
+
if (OUTCOMES_HEADING.test(clean))
|
|
34
|
+
continue;
|
|
35
|
+
if (OTHER_HEADING.test(clean)) {
|
|
36
|
+
if (hasOutcomesHeading)
|
|
37
|
+
break;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const outcome = stripPrefix(line);
|
|
41
|
+
if (outcome.length < 8)
|
|
42
|
+
continue;
|
|
43
|
+
const key = outcome.toLowerCase();
|
|
44
|
+
if (seen.has(key))
|
|
45
|
+
continue;
|
|
46
|
+
seen.add(key);
|
|
47
|
+
texts.push(outcome);
|
|
48
|
+
}
|
|
49
|
+
return texts.map((outcomeText, index) => ({ id: `o${index}`, text: outcomeText }));
|
|
50
|
+
}
|
|
51
|
+
/** A line reduced to its section-heading text, or '' if it is not a heading. */
|
|
52
|
+
function heading(line) {
|
|
53
|
+
return line
|
|
54
|
+
.replace(/^[#*\s]+/, '')
|
|
55
|
+
.replace(/[*\s]+$/, '')
|
|
56
|
+
.replace(/:$/, '')
|
|
57
|
+
.trim();
|
|
58
|
+
}
|
|
59
|
+
function stripPrefix(line) {
|
|
60
|
+
return line
|
|
61
|
+
.replace(/^\s*(?:[-*•·▪]|\d+(?:\.\d+)*[.)]?)\s*/, '')
|
|
62
|
+
.replace(/^(?:LO|ILO|CLO|ULO|SLO)\s*\d+\s*[:.)-]?\s*/i, '')
|
|
63
|
+
.replace(/^students?\s+(?:will|should)\s+be\s+able\s+to\s+/i, '')
|
|
64
|
+
.replace(/^students?\s+can\s+/i, '')
|
|
65
|
+
.replace(/^be\s+able\s+to\s+/i, '')
|
|
66
|
+
.trim();
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build one `/v1/systemone` request: a Choice per resource over the outcome
|
|
70
|
+
* list plus `none`. Choice probabilities let us keep near-ties instead of
|
|
71
|
+
* blindly trusting a `none` that won by a rounding error.
|
|
72
|
+
*/
|
|
73
|
+
export function buildAlignmentRequest(topic, resources, outcomes, model) {
|
|
74
|
+
const outcomeList = outcomes.slice(0, MAX_ALIGN_OUTCOMES);
|
|
75
|
+
const resourceList = resources.slice(0, MAX_ALIGN_RESOURCES);
|
|
76
|
+
const resourceById = new Map();
|
|
77
|
+
const outcomeById = new Map();
|
|
78
|
+
const criteria = {};
|
|
79
|
+
for (const outcome of outcomeList) {
|
|
80
|
+
outcomeById.set(outcome.id, outcome);
|
|
81
|
+
criteria[outcome.id] = outcome.text.slice(0, MAX_TEXT_CHARS);
|
|
82
|
+
}
|
|
83
|
+
criteria.none = 'None of the outcomes fit this resource.';
|
|
84
|
+
const questions = {};
|
|
85
|
+
const stateResources = [];
|
|
86
|
+
resourceList.forEach((resource, index) => {
|
|
87
|
+
const id = `r${index}`;
|
|
88
|
+
resourceById.set(id, resource);
|
|
89
|
+
stateResources.push({
|
|
90
|
+
id,
|
|
91
|
+
title: resource.title.slice(0, 180),
|
|
92
|
+
venue: resource.venue?.slice(0, 80),
|
|
93
|
+
description: resource.annotation.text?.slice(0, MAX_TEXT_CHARS),
|
|
94
|
+
});
|
|
95
|
+
questions[id] = {
|
|
96
|
+
type: 'choice',
|
|
97
|
+
instructions: `Which \`outcomes\` entry does \`resources[${index}]\` best help a ` +
|
|
98
|
+
`lecturer teach? Choose \`none\` if no outcome fits.`,
|
|
99
|
+
criteria,
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
request: {
|
|
104
|
+
model,
|
|
105
|
+
state: {
|
|
106
|
+
topic: topic.slice(0, 300),
|
|
107
|
+
outcomes: outcomeList.map((outcome) => ({
|
|
108
|
+
id: outcome.id,
|
|
109
|
+
text: outcome.text.slice(0, MAX_TEXT_CHARS),
|
|
110
|
+
})),
|
|
111
|
+
resources: stateResources,
|
|
112
|
+
},
|
|
113
|
+
questions,
|
|
114
|
+
keep_alive: '5m',
|
|
115
|
+
},
|
|
116
|
+
resourceById,
|
|
117
|
+
outcomeById,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export function parseAlignmentResponse(response, resourceById, outcomeById) {
|
|
121
|
+
const answers = response?.answers ?? {};
|
|
122
|
+
const result = new Map();
|
|
123
|
+
for (const [resourceId, resource] of resourceById) {
|
|
124
|
+
const answer = answers[resourceId];
|
|
125
|
+
if (!answer || answer.type !== 'choice')
|
|
126
|
+
continue;
|
|
127
|
+
const probabilities = answer.probabilities ?? {};
|
|
128
|
+
const noneProbability = probabilities.none ?? 0;
|
|
129
|
+
let chosen = answer.choice ?? 'none';
|
|
130
|
+
let forcedLow = false;
|
|
131
|
+
if (chosen === 'none') {
|
|
132
|
+
const best = [...outcomeById.keys()]
|
|
133
|
+
.map((id) => ({ id, probability: probabilities[id] ?? 0 }))
|
|
134
|
+
.sort((a, b) => b.probability - a.probability)[0];
|
|
135
|
+
// The small decision model sometimes puts `none` a hair ahead of a
|
|
136
|
+
// genuinely close outcome. Keep it as a low-confidence proposal.
|
|
137
|
+
if (best && best.probability >= 0.35 && best.probability >= noneProbability * 0.8) {
|
|
138
|
+
chosen = best.id;
|
|
139
|
+
forcedLow = true;
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const outcome = outcomeById.get(chosen);
|
|
146
|
+
if (!outcome)
|
|
147
|
+
continue;
|
|
148
|
+
const score = probabilities[chosen] ?? answer.confidence ?? 0;
|
|
149
|
+
result.set(resource, {
|
|
150
|
+
outcomeId: outcome.id,
|
|
151
|
+
outcomeText: outcome.text,
|
|
152
|
+
score,
|
|
153
|
+
confidence: forcedLow ? 'low' : confidenceBand(score),
|
|
154
|
+
proposedBy: 'decision',
|
|
155
|
+
confirmed: false,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Local System One alignment (ADR-0017): one Choice per resource. Preferred
|
|
162
|
+
* because it reasons about fit rather than surface similarity.
|
|
163
|
+
*/
|
|
164
|
+
export class OllamaOutcomeAligner {
|
|
165
|
+
baseUrl;
|
|
166
|
+
model;
|
|
167
|
+
apiKey;
|
|
168
|
+
name = 'decision';
|
|
169
|
+
constructor(baseUrl, model = 'tev1:0.8b', apiKey) {
|
|
170
|
+
this.baseUrl = baseUrl;
|
|
171
|
+
this.model = model;
|
|
172
|
+
this.apiKey = apiKey;
|
|
173
|
+
}
|
|
174
|
+
async align(topic, resources, outcomes) {
|
|
175
|
+
const { request, resourceById, outcomeById } = buildAlignmentRequest(topic, resources, outcomes, this.model);
|
|
176
|
+
if (!Object.keys(request.questions).length)
|
|
177
|
+
return new Map();
|
|
178
|
+
const headers = { 'content-type': 'application/json' };
|
|
179
|
+
if (this.apiKey)
|
|
180
|
+
headers.authorization = `Bearer ${this.apiKey}`;
|
|
181
|
+
const res = await fetch(`${this.baseUrl.replace(/\/$/, '')}/v1/systemone`, {
|
|
182
|
+
method: 'POST',
|
|
183
|
+
headers,
|
|
184
|
+
body: JSON.stringify(request),
|
|
185
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
186
|
+
});
|
|
187
|
+
if (!res.ok)
|
|
188
|
+
throw new Error(`Ollama System One HTTP ${res.status}`);
|
|
189
|
+
return parseAlignmentResponse(await res.json(), resourceById, outcomeById);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Embedding fallback: cosine similarity between each resource and outcome.
|
|
194
|
+
* Less precise than a Choice, but works anywhere embeddings do.
|
|
195
|
+
*/
|
|
196
|
+
export class EmbeddingOutcomeAligner {
|
|
197
|
+
provider;
|
|
198
|
+
name = 'embeddings';
|
|
199
|
+
constructor(provider) {
|
|
200
|
+
this.provider = provider;
|
|
201
|
+
}
|
|
202
|
+
async align(_topic, resources, outcomes) {
|
|
203
|
+
const outcomeList = outcomes.slice(0, MAX_ALIGN_OUTCOMES);
|
|
204
|
+
const resourceList = resources.slice(0, MAX_ALIGN_RESOURCES);
|
|
205
|
+
if (!outcomeList.length || !resourceList.length)
|
|
206
|
+
return new Map();
|
|
207
|
+
const texts = [
|
|
208
|
+
...outcomeList.map((outcome) => outcome.text),
|
|
209
|
+
...resourceList.map(resourceText),
|
|
210
|
+
];
|
|
211
|
+
const vectors = await this.provider.embed(texts);
|
|
212
|
+
if (vectors.length !== texts.length) {
|
|
213
|
+
throw new Error('embedding count did not match the request');
|
|
214
|
+
}
|
|
215
|
+
const outcomeVectors = vectors.slice(0, outcomeList.length);
|
|
216
|
+
const resourceVectors = vectors.slice(outcomeList.length);
|
|
217
|
+
const result = new Map();
|
|
218
|
+
resourceList.forEach((resource, resourceIndex) => {
|
|
219
|
+
let bestOutcome = null;
|
|
220
|
+
let bestScore = -1;
|
|
221
|
+
for (let outcomeIndex = 0; outcomeIndex < outcomeList.length; outcomeIndex++) {
|
|
222
|
+
const score = cosineSimilarity(resourceVectors[resourceIndex], outcomeVectors[outcomeIndex]);
|
|
223
|
+
if (score > bestScore) {
|
|
224
|
+
bestScore = score;
|
|
225
|
+
bestOutcome = outcomeList[outcomeIndex];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (bestOutcome && bestScore >= EMBEDDING_MIN_SCORE) {
|
|
229
|
+
result.set(resource, {
|
|
230
|
+
outcomeId: bestOutcome.id,
|
|
231
|
+
outcomeText: bestOutcome.text,
|
|
232
|
+
score: bestScore,
|
|
233
|
+
confidence: confidenceBand(bestScore),
|
|
234
|
+
proposedBy: 'embeddings',
|
|
235
|
+
confirmed: false,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/** Pick the configured aligner: decision model first, embeddings second. */
|
|
243
|
+
export function createOutcomeAligner(config) {
|
|
244
|
+
if (config.decisionUrl) {
|
|
245
|
+
return new OllamaOutcomeAligner(config.decisionUrl, config.decisionModel ?? 'tev1:0.8b', config.decisionApiKey);
|
|
246
|
+
}
|
|
247
|
+
if (config.embeddingUrl) {
|
|
248
|
+
const provider = createEmbeddingProvider(config);
|
|
249
|
+
return provider ? new EmbeddingOutcomeAligner(provider) : null;
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
function resourceText(resource) {
|
|
254
|
+
return [resource.title, resource.venue, resource.annotation.text]
|
|
255
|
+
.filter(Boolean)
|
|
256
|
+
.join('. ')
|
|
257
|
+
.slice(0, 400);
|
|
258
|
+
}
|
|
259
|
+
function confidenceBand(score) {
|
|
260
|
+
if (score >= 0.7)
|
|
261
|
+
return 'high';
|
|
262
|
+
if (score >= 0.45)
|
|
263
|
+
return 'medium';
|
|
264
|
+
return 'low';
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=outcomes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcomes.js","sourceRoot":"","sources":["../../src/align/outcomes.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,uBAAuB,EAA0B,MAAM,uBAAuB,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,UAAU,GAAG,MAAM,CAAC;AAC1B,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AACtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AACpC,MAAM,cAAc,GAAG,GAAG,CAAC;AAC3B,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAEjC,MAAM,gBAAgB,GACpB,+HAA+H,CAAC;AAClI,MAAM,aAAa,GACjB,kKAAkK,CAAC;AAErK;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,IAAI,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7C,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,MAAM;QACR,CAAC;IACH,CAAC;IACD,MAAM,kBAAkB,GAAG,KAAK,GAAG,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,SAAS;QAC3C,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,IAAI,kBAAkB;gBAAE,MAAM;YAC9B,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QACjC,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,gFAAgF;AAChF,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI;SACR,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;SACjB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI;SACR,OAAO,CAAC,uCAAuC,EAAE,EAAE,CAAC;SACpD,OAAO,CAAC,6CAA6C,EAAE,EAAE,CAAC;SAC1D,OAAO,CAAC,mDAAmD,EAAE,EAAE,CAAC;SAChE,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC;SACnC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC;SAClC,IAAI,EAAE,CAAC;AACZ,CAAC;AA8BD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAa,EACb,SAAqB,EACrB,QAAmB,EACnB,KAAa;IAEb,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;IAC1D,MAAM,YAAY,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC;IAC7D,MAAM,YAAY,GAAG,IAAI,GAAG,EAAoB,CAAC;IACjD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAmB,CAAC;IAE/C,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;QAClC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IAC/D,CAAC;IACD,QAAQ,CAAC,IAAI,GAAG,yCAAyC,CAAC;IAE1D,MAAM,SAAS,GAAsC,EAAE,CAAC;IACxD,MAAM,cAAc,GAA2C,EAAE,CAAC;IAClE,YAAY,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QACvC,MAAM,EAAE,GAAG,IAAI,KAAK,EAAE,CAAC;QACvB,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC/B,cAAc,CAAC,IAAI,CAAC;YAClB,EAAE;YACF,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;YACnC,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;YACnC,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC;SAChE,CAAC,CAAC;QACH,SAAS,CAAC,EAAE,CAAC,GAAG;YACd,IAAI,EAAE,QAAQ;YACd,YAAY,EACV,6CAA6C,KAAK,kBAAkB;gBACpE,qDAAqD;YACvD,QAAQ;SACT,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,OAAO,EAAE;YACP,KAAK;YACL,KAAK,EAAE;gBACL,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;gBAC1B,QAAQ,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;oBACtC,EAAE,EAAE,OAAO,CAAC,EAAE;oBACd,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC;iBAC5C,CAAC,CAAC;gBACH,SAAS,EAAE,cAAc;aAC1B;YACD,SAAS;YACT,UAAU,EAAE,IAAI;SACjB;QACD,YAAY;QACZ,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,QAAiB,EACjB,YAAmC,EACnC,WAAiC;IAEjC,MAAM,OAAO,GACV,QAUC,EAAE,OAAO,IAAI,EAAE,CAAC;IAEpB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC9C,KAAK,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,YAAY,EAAE,CAAC;QAClD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QAClD,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC;QACjD,MAAM,eAAe,GAAG,aAAa,CAAC,IAAI,IAAI,CAAC,CAAC;QAChD,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC;QACrC,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,CAAC,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;iBACjC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,aAAa,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;iBAC1D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;YACpD,mEAAmE;YACnE,iEAAiE;YACjE,IAAI,IAAI,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,IAAI,IAAI,CAAC,WAAW,IAAI,eAAe,GAAG,GAAG,EAAE,CAAC;gBAClF,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;gBACjB,SAAS,GAAG,IAAI,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,SAAS;YACX,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE;YACnB,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,WAAW,EAAE,OAAO,CAAC,IAAI;YACzB,KAAK;YACL,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC;YACrD,UAAU,EAAE,UAAU;YACtB,SAAS,EAAE,KAAK;SACjB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAWD;;;GAGG;AACH,MAAM,OAAO,oBAAoB;IAIZ;IACA;IACA;IALV,IAAI,GAAG,UAAU,CAAC;IAE3B,YACmB,OAAe,EACf,QAAgB,WAAW,EAC3B,MAAe;QAFf,YAAO,GAAP,OAAO,CAAQ;QACf,UAAK,GAAL,KAAK,CAAsB;QAC3B,WAAM,GAAN,MAAM,CAAS;IAC/B,CAAC;IAEJ,KAAK,CAAC,KAAK,CACT,KAAa,EACb,SAAqB,EACrB,QAAmB;QAEnB,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,qBAAqB,CAClE,KAAK,EACL,SAAS,EACT,QAAQ,EACR,IAAI,CAAC,KAAK,CACX,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM;YAAE,OAAO,IAAI,GAAG,EAAE,CAAC;QAE7D,MAAM,OAAO,GAA2B,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;QAC/E,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,aAAa,GAAG,UAAU,IAAI,CAAC,MAAM,EAAE,CAAC;QAEjE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,EAAE;YACzE,MAAM,EAAE,MAAM;YACd,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;YAC7B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC;SACxC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACrE,OAAO,sBAAsB,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;IAC7E,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,uBAAuB;IAGL;IAFpB,IAAI,GAAG,YAAY,CAAC;IAE7B,YAA6B,QAA2B;QAA3B,aAAQ,GAAR,QAAQ,CAAmB;IAAG,CAAC;IAE5D,KAAK,CAAC,KAAK,CACT,MAAc,EACd,SAAqB,EACrB,QAAmB;QAEnB,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;QAC1D,MAAM,YAAY,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC;QAC7D,IAAI,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM;YAAE,OAAO,IAAI,GAAG,EAAE,CAAC;QAElE,MAAM,KAAK,GAAG;YACZ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;YAC7C,GAAG,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC;SAClC,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACjD,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QACD,MAAM,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QAC5D,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAE1D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;QAC9C,YAAY,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE;YAC/C,IAAI,WAAW,GAAmB,IAAI,CAAC;YACvC,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;YACnB,KAAK,IAAI,YAAY,GAAG,CAAC,EAAE,YAAY,GAAG,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,CAAC;gBAC7E,MAAM,KAAK,GAAG,gBAAgB,CAC5B,eAAe,CAAC,aAAa,CAAC,EAC9B,cAAc,CAAC,YAAY,CAAC,CAC7B,CAAC;gBACF,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;oBACtB,SAAS,GAAG,KAAK,CAAC;oBAClB,WAAW,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;gBAC1C,CAAC;YACH,CAAC;YACD,IAAI,WAAW,IAAI,SAAS,IAAI,mBAAmB,EAAE,CAAC;gBACpD,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE;oBACnB,SAAS,EAAE,WAAW,CAAC,EAAE;oBACzB,WAAW,EAAE,WAAW,CAAC,IAAI;oBAC7B,KAAK,EAAE,SAAS;oBAChB,UAAU,EAAE,cAAc,CAAC,SAAS,CAAC;oBACrC,UAAU,EAAE,YAAY;oBACxB,SAAS,EAAE,KAAK;iBACjB,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AAED,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAAC,MAAsB;IACzD,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,OAAO,IAAI,oBAAoB,CAC7B,MAAM,CAAC,WAAW,EAClB,MAAM,CAAC,aAAa,IAAI,WAAW,EACnC,MAAM,CAAC,cAAc,CACtB,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;QACjD,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,uBAAuB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,QAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;SAC9D,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC;SACV,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,IAAI,KAAK,IAAI,GAAG;QAAE,OAAO,MAAM,CAAC;IAChC,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,QAAQ,CAAC;IACnC,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Resource } from '../types.js';
|
|
2
|
+
import type { PipelineConfig } from '../config.js';
|
|
3
|
+
/** The prompt for a one-line "why this outcome" reason. */
|
|
4
|
+
export declare function buildAlignmentReasonPrompt(topic: string, outcomeText: string, resource: Resource): string;
|
|
5
|
+
export declare function generateAlignmentReason(topic: string, outcomeText: string, resource: Resource, config: PipelineConfig): Promise<string | undefined>;
|
|
6
|
+
/**
|
|
7
|
+
* Fill in `alignment.reason` for up to a few resources with the local LLM.
|
|
8
|
+
* Optional and non-fatal: no LLM, a timeout or a bad response simply leaves
|
|
9
|
+
* the reason absent.
|
|
10
|
+
*/
|
|
11
|
+
export declare function addAlignmentReasons(resources: Resource[], topic: string, config: PipelineConfig): Promise<void>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const TIMEOUT_MS = 12_000;
|
|
2
|
+
// Alignment is capped at 12 resources; generating reasons for all of them
|
|
3
|
+
// with a local LLM is slow, so only the first few proposals get prose.
|
|
4
|
+
const MAX_REASONS = 6;
|
|
5
|
+
const REASON_CONCURRENCY = 3;
|
|
6
|
+
/** The prompt for a one-line "why this outcome" reason. */
|
|
7
|
+
export function buildAlignmentReasonPrompt(topic, outcomeText, resource) {
|
|
8
|
+
const meta = [
|
|
9
|
+
resource.title,
|
|
10
|
+
resource.venue ? `Venue: ${resource.venue}` : null,
|
|
11
|
+
resource.year ? `Year: ${resource.year}` : null,
|
|
12
|
+
resource.annotation.text
|
|
13
|
+
? `Description: ${resource.annotation.text.slice(0, 400)}`
|
|
14
|
+
: null,
|
|
15
|
+
]
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
.join('\n');
|
|
18
|
+
return (`A lecturer is teaching: "${topic.slice(0, 300)}"\n\n` +
|
|
19
|
+
`Unit outcome: "${outcomeText.slice(0, 300)}"\n\n` +
|
|
20
|
+
`Resource:\n${meta}\n\n` +
|
|
21
|
+
`In one short sentence, say specifically how this resource helps students ` +
|
|
22
|
+
`meet that outcome. Do not invent details not present above. ` +
|
|
23
|
+
`Reply with the sentence only.`);
|
|
24
|
+
}
|
|
25
|
+
export async function generateAlignmentReason(topic, outcomeText, resource, config) {
|
|
26
|
+
if (!config.ollamaUrl)
|
|
27
|
+
return undefined;
|
|
28
|
+
const res = await fetch(`${config.ollamaUrl.replace(/\/$/, '')}/api/generate`, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: {
|
|
31
|
+
'content-type': 'application/json',
|
|
32
|
+
...(config.ollamaApiKey
|
|
33
|
+
? { authorization: `Bearer ${config.ollamaApiKey}` }
|
|
34
|
+
: {}),
|
|
35
|
+
},
|
|
36
|
+
body: JSON.stringify({
|
|
37
|
+
model: config.ollamaModel ?? 'llama3.1:8b',
|
|
38
|
+
prompt: buildAlignmentReasonPrompt(topic, outcomeText, resource),
|
|
39
|
+
stream: false,
|
|
40
|
+
think: false,
|
|
41
|
+
options: { num_predict: 80, temperature: 0.2 },
|
|
42
|
+
}),
|
|
43
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok)
|
|
46
|
+
throw new Error(`Ollama HTTP ${res.status}`);
|
|
47
|
+
const data = (await res.json());
|
|
48
|
+
const line = data.response?.trim().split('\n')[0]?.trim();
|
|
49
|
+
return line ? line.slice(0, 240) : undefined;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Fill in `alignment.reason` for up to a few resources with the local LLM.
|
|
53
|
+
* Optional and non-fatal: no LLM, a timeout or a bad response simply leaves
|
|
54
|
+
* the reason absent.
|
|
55
|
+
*/
|
|
56
|
+
export async function addAlignmentReasons(resources, topic, config) {
|
|
57
|
+
if (!config.ollamaUrl || config.alignmentReasons === false)
|
|
58
|
+
return;
|
|
59
|
+
const targets = resources.filter((resource) => resource.alignment).slice(0, MAX_REASONS);
|
|
60
|
+
if (!targets.length)
|
|
61
|
+
return;
|
|
62
|
+
await mapLimit(targets, REASON_CONCURRENCY, async (resource) => {
|
|
63
|
+
const alignment = resource.alignment;
|
|
64
|
+
try {
|
|
65
|
+
const reason = await generateAlignmentReason(topic, alignment.outcomeText, resource, config);
|
|
66
|
+
if (reason)
|
|
67
|
+
alignment.reason = reason;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// reasons are a bonus, never a failure
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async function mapLimit(items, limit, fn) {
|
|
75
|
+
let next = 0;
|
|
76
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
|
|
77
|
+
while (next < items.length) {
|
|
78
|
+
const i = next++;
|
|
79
|
+
await fn(items[i]);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
await Promise.all(workers);
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=reasons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasons.js","sourceRoot":"","sources":["../../src/align/reasons.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,4 +8,4 @@ import type { PipelineConfig } from '../config.js';
|
|
|
8
8
|
* 2. extractive description from retrieval/page metadata
|
|
9
9
|
* 3. nothing — metadata-only result
|
|
10
10
|
*/
|
|
11
|
-
export declare function annotate(topic: string, candidate: Candidate, config: PipelineConfig, llmWanted: boolean, stance?: 'supporting' | 'counterpoint'): Promise<Annotation>;
|
|
11
|
+
export declare function annotate(topic: string, candidate: Candidate, config: PipelineConfig, llmWanted: boolean, stance?: 'supporting' | 'counterpoint', context?: string): Promise<Annotation>;
|
package/dist/annotate/ladder.js
CHANGED
|
@@ -6,12 +6,12 @@ import { generateRationale } from './ollama.js';
|
|
|
6
6
|
* 2. extractive description from retrieval/page metadata
|
|
7
7
|
* 3. nothing — metadata-only result
|
|
8
8
|
*/
|
|
9
|
-
export async function annotate(topic, candidate, config, llmWanted, stance = 'supporting') {
|
|
9
|
+
export async function annotate(topic, candidate, config, llmWanted, stance = 'supporting', context) {
|
|
10
10
|
if (llmWanted && config.ollamaUrl) {
|
|
11
11
|
try {
|
|
12
12
|
return {
|
|
13
13
|
source: 'llm',
|
|
14
|
-
text: await generateRationale(topic, candidate, config, stance),
|
|
14
|
+
text: await generateRationale(topic, candidate, config, stance, context),
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ladder.js","sourceRoot":"","sources":["../../src/annotate/ladder.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,KAAa,EACb,SAAoB,EACpB,MAAsB,EACtB,SAAkB,EAClB,SAAwC,YAAY;
|
|
1
|
+
{"version":3,"file":"ladder.js","sourceRoot":"","sources":["../../src/annotate/ladder.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,KAAa,EACb,SAAoB,EACpB,MAAsB,EACtB,SAAkB,EAClB,SAAwC,YAAY,EACpD,OAAgB;IAEhB,IAAI,SAAS,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,OAAO;gBACL,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,MAAM,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;aACzE,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;IAChD,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC;IACjE,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxC,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAW;IACtC,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC;AACnE,CAAC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { Candidate } from '../retrieval/candidates.js';
|
|
2
2
|
import type { PipelineConfig } from '../config.js';
|
|
3
|
+
/** The rationale prompt, including optional teaching context (ADR-0018). */
|
|
4
|
+
export declare function buildRationalePrompt(topic: string, candidate: Candidate, stance?: 'supporting' | 'counterpoint', context?: string): string;
|
|
3
5
|
/**
|
|
4
6
|
* Generate a one-to-two sentence relational rationale (ADR-0011 rung 1).
|
|
5
7
|
* Prompt is sized for 4B–8B models (spec §8): short, concrete, no format
|
|
6
8
|
* tricks. The LLM only writes ABOUT a resource retrieval already found —
|
|
7
9
|
* it can never introduce one.
|
|
8
10
|
*/
|
|
9
|
-
export declare function generateRationale(topic: string, candidate: Candidate, config: PipelineConfig, stance?: 'supporting' | 'counterpoint'): Promise<string>;
|
|
11
|
+
export declare function generateRationale(topic: string, candidate: Candidate, config: PipelineConfig, stance?: 'supporting' | 'counterpoint', context?: string): Promise<string>;
|
package/dist/annotate/ollama.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
const GENERATE_TIMEOUT_MS = 30_000;
|
|
2
2
|
const DEFAULT_MODEL = 'llama3.1:8b';
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
* Prompt is sized for 4B–8B models (spec §8): short, concrete, no format
|
|
6
|
-
* tricks. The LLM only writes ABOUT a resource retrieval already found —
|
|
7
|
-
* it can never introduce one.
|
|
8
|
-
*/
|
|
9
|
-
export async function generateRationale(topic, candidate, config, stance = 'supporting') {
|
|
10
|
-
if (!config.ollamaUrl)
|
|
11
|
-
throw new Error('no Ollama configured');
|
|
3
|
+
/** The rationale prompt, including optional teaching context (ADR-0018). */
|
|
4
|
+
export function buildRationalePrompt(topic, candidate, stance = 'supporting', context) {
|
|
12
5
|
const meta = [
|
|
13
6
|
`Title: ${candidate.title}`,
|
|
14
7
|
candidate.authors.length ? `By: ${candidate.authors.slice(0, 3).join(', ')}` : null,
|
|
@@ -24,11 +17,23 @@ export async function generateRationale(topic, candidate, config, stance = 'supp
|
|
|
24
17
|
`and why showing students a counterpoint here could be valuable.`
|
|
25
18
|
: `In one or two sentences, say why this resource could support that teaching, ` +
|
|
26
19
|
`what it adds, and (if apparent) what it does not cover.`;
|
|
27
|
-
|
|
20
|
+
return ((context ? `Teaching context: ${context.slice(0, 400)}\n\n` : '') +
|
|
21
|
+
`A lecturer is teaching: "${topic.slice(0, 300)}"\n\n` +
|
|
28
22
|
`Candidate ${stance === 'counterpoint' ? 'counterpoint' : 'supporting'} resource:\n${meta}\n\n` +
|
|
29
23
|
`${ask} ` +
|
|
30
24
|
`Be concrete and honest; do not invent details not present above. ` +
|
|
31
|
-
`Reply with the sentences only
|
|
25
|
+
`Reply with the sentences only.`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generate a one-to-two sentence relational rationale (ADR-0011 rung 1).
|
|
29
|
+
* Prompt is sized for 4B–8B models (spec §8): short, concrete, no format
|
|
30
|
+
* tricks. The LLM only writes ABOUT a resource retrieval already found —
|
|
31
|
+
* it can never introduce one.
|
|
32
|
+
*/
|
|
33
|
+
export async function generateRationale(topic, candidate, config, stance = 'supporting', context) {
|
|
34
|
+
if (!config.ollamaUrl)
|
|
35
|
+
throw new Error('no Ollama configured');
|
|
36
|
+
const prompt = buildRationalePrompt(topic, candidate, stance, context);
|
|
32
37
|
const res = await fetch(`${config.ollamaUrl.replace(/\/$/, '')}/api/generate`, {
|
|
33
38
|
method: 'POST',
|
|
34
39
|
headers: {
|
|
@@ -39,6 +44,7 @@ export async function generateRationale(topic, candidate, config, stance = 'supp
|
|
|
39
44
|
model: config.ollamaModel ?? DEFAULT_MODEL,
|
|
40
45
|
prompt,
|
|
41
46
|
stream: false,
|
|
47
|
+
think: false,
|
|
42
48
|
options: { num_predict: 120, temperature: 0.3 },
|
|
43
49
|
}),
|
|
44
50
|
signal: AbortSignal.timeout(GENERATE_TIMEOUT_MS),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/annotate/ollama.ts"],"names":[],"mappings":"AAGA,MAAM,mBAAmB,GAAG,MAAM,CAAC;AACnC,MAAM,aAAa,GAAG,aAAa,CAAC;AAEpC
|
|
1
|
+
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/annotate/ollama.ts"],"names":[],"mappings":"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"}
|
package/dist/batch.d.ts
CHANGED
|
File without changes
|
package/dist/batch.js
CHANGED
|
File without changes
|
package/dist/batch.js.map
CHANGED
|
File without changes
|
package/dist/brief.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { SearchConstraints } from './types.js';
|
|
2
|
+
import type { Candidate } from './retrieval/candidates.js';
|
|
3
|
+
/** The four deterministic interview questions (ADR-0018). */
|
|
4
|
+
export interface BriefAnswers {
|
|
5
|
+
stance: 'support' | 'challenge' | 'both';
|
|
6
|
+
priorKnowledge: 'none' | 'reading' | 'lecture' | 'notes';
|
|
7
|
+
/** Optional free-text clarification about prior knowledge. */
|
|
8
|
+
priorNote?: string;
|
|
9
|
+
timing: 'pre-class' | 'in-class' | 'post-class' | 'assessment';
|
|
10
|
+
openAccessOnly?: boolean;
|
|
11
|
+
noVideo?: boolean;
|
|
12
|
+
recentOnly?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface CompiledBrief {
|
|
15
|
+
counterpoint: boolean;
|
|
16
|
+
constraints: SearchConstraints;
|
|
17
|
+
/** Teaching context passed to the annotator. Retrieval is unchanged. */
|
|
18
|
+
context: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Turn the interview answers into the fields the pipeline already understands:
|
|
22
|
+
* a counterpoint flag, search constraints and a teaching-context string.
|
|
23
|
+
*/
|
|
24
|
+
export declare function compileBrief(answers: BriefAnswers, currentYear?: number): CompiledBrief;
|
|
25
|
+
/**
|
|
26
|
+
* Apply brief constraints to candidates before ranking. `openAccessOnly` is
|
|
27
|
+
* best-effort: only papers explicitly flagged as subscription are dropped;
|
|
28
|
+
* unknown access is kept (and labelled later).
|
|
29
|
+
*/
|
|
30
|
+
export declare function applyConstraints(candidates: Candidate[], constraints?: SearchConstraints): Candidate[];
|