@dxos/assistant 0.10.0 → 0.11.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/dist/lib/extraction.mjs +169 -0
- package/dist/lib/extraction.mjs.map +1 -0
- package/dist/lib/index.mjs +1958 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/types/src/request/AiRequest.d.ts +6 -3
- package/dist/types/src/request/AiRequest.d.ts.map +1 -1
- package/dist/types/src/request/AiRequest.test.d.ts +2 -0
- package/dist/types/src/request/AiRequest.test.d.ts.map +1 -0
- package/dist/types/src/request/format.d.ts +2 -2
- package/dist/types/src/request/format.d.ts.map +1 -1
- package/dist/types/src/request/format.test.d.ts +2 -0
- package/dist/types/src/request/format.test.d.ts.map +1 -0
- package/dist/types/src/session/AiContext.d.ts +1 -1
- package/dist/types/src/session/AiContext.d.ts.map +1 -1
- package/dist/types/src/session/AiSession.d.ts +25 -4
- package/dist/types/src/session/AiSession.d.ts.map +1 -1
- package/dist/types/src/session/harness-control.d.ts +4 -0
- package/dist/types/src/session/harness-control.d.ts.map +1 -1
- package/dist/types/src/tool-runtime/services.d.ts +7 -0
- package/dist/types/src/tool-runtime/services.d.ts.map +1 -1
- package/dist/types/src/tool-runtime/services.test.d.ts +2 -0
- package/dist/types/src/tool-runtime/services.test.d.ts.map +1 -0
- package/dist/types/src/util/execution-graph.d.ts.map +1 -1
- package/dist/types/src/util/tracing.d.ts +10 -0
- package/dist/types/src/util/tracing.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +25 -25
- package/src/extraction/quotes.test.ts +4 -4
- package/src/request/AiRequest.test.ts +110 -0
- package/src/request/AiRequest.ts +11 -4
- package/src/request/format.test.ts +93 -0
- package/src/request/format.ts +48 -11
- package/src/session/AiContext.ts +1 -1
- package/src/session/AiSession.test.ts +124 -0
- package/src/session/AiSession.ts +81 -8
- package/src/tool-runtime/services.test.ts +41 -0
- package/src/tool-runtime/services.ts +16 -1
- package/src/util/execution-graph.ts +1 -1
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
- package/dist/lib/neutral/extraction/index.mjs +0 -197
- package/dist/lib/neutral/extraction/index.mjs.map +0 -7
- package/dist/lib/neutral/index.mjs +0 -2047
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
package/src/session/AiSession.ts
CHANGED
|
@@ -4,22 +4,24 @@
|
|
|
4
4
|
|
|
5
5
|
// @import-as-namespace
|
|
6
6
|
|
|
7
|
-
import { type Registry as AtomRegistry } from '@effect-atom/atom
|
|
7
|
+
import { type Registry as AtomRegistry } from '@effect-atom/atom';
|
|
8
8
|
import * as Array from 'effect/Array';
|
|
9
9
|
import * as Effect from 'effect/Effect';
|
|
10
10
|
import * as Either from 'effect/Either';
|
|
11
11
|
import { pipe } from 'effect/Function';
|
|
12
12
|
import * as Layer from 'effect/Layer';
|
|
13
|
+
import * as Order from 'effect/Order';
|
|
13
14
|
import * as Record from 'effect/Record';
|
|
14
15
|
import * as Runtime from 'effect/Runtime';
|
|
15
16
|
|
|
16
17
|
import { type OpaqueToolkit, type ToolExecutionService, type ToolResolverService } from '@dxos/ai';
|
|
17
|
-
import { McpServer, Operation, type Skill, Trace } from '@dxos/compute';
|
|
18
|
+
import { type Instructions, McpServer, Operation, type Skill, Trace } from '@dxos/compute';
|
|
18
19
|
import { Resource } from '@dxos/context';
|
|
19
20
|
import { Database, Feed, Filter, Obj, Registry } from '@dxos/echo';
|
|
20
21
|
import { invariant } from '@dxos/invariant';
|
|
21
22
|
import { log } from '@dxos/log';
|
|
22
23
|
import { McpToolkit } from '@dxos/mcp-client';
|
|
24
|
+
import { FeedProtocol } from '@dxos/protocols';
|
|
23
25
|
import { type ContentBlock, Message } from '@dxos/types';
|
|
24
26
|
|
|
25
27
|
import { AiRequest, type GenerationObserver, formatSystemPrompt } from '../request';
|
|
@@ -55,6 +57,11 @@ export type Options = {
|
|
|
55
57
|
runtime: Runtime.Runtime<Database.Service>;
|
|
56
58
|
/** @effect-atom/atom-react Registry for reactive state. */
|
|
57
59
|
registry?: AtomRegistry.Registry;
|
|
60
|
+
/**
|
|
61
|
+
* Instructions steering the conversation (typically the owning `Chat`'s), rendered into the system
|
|
62
|
+
* prompt on every turn. The session is feed-centric and cannot reach its chat, so these are passed in.
|
|
63
|
+
*/
|
|
64
|
+
instructions?: readonly Instructions.Instructions[];
|
|
58
65
|
};
|
|
59
66
|
|
|
60
67
|
/**
|
|
@@ -68,18 +75,22 @@ const SUMMARY_THRESHOLD = 80_000;
|
|
|
68
75
|
* Executes tools based on AI responses and supports cancellation of in-progress requests.
|
|
69
76
|
*/
|
|
70
77
|
export class Session extends Resource {
|
|
78
|
+
private readonly _feed: Feed.Feed;
|
|
79
|
+
private readonly _runtime: Runtime.Runtime<Database.Service>;
|
|
80
|
+
readonly #instructions: readonly Instructions.Instructions[];
|
|
81
|
+
|
|
71
82
|
/**
|
|
72
83
|
* Skills and objects bound to the session.
|
|
73
84
|
*/
|
|
74
85
|
private readonly _binder: AiContext.Binder;
|
|
75
|
-
|
|
76
|
-
private readonly _runtime: Runtime.Runtime<Database.Service>;
|
|
86
|
+
|
|
77
87
|
private readonly _sessionLoader = new SessionLoader();
|
|
78
88
|
|
|
79
89
|
public constructor(options: Options) {
|
|
80
90
|
super();
|
|
81
91
|
this._feed = options.feed;
|
|
82
92
|
this._runtime = options.runtime;
|
|
93
|
+
this.#instructions = options.instructions ?? [];
|
|
83
94
|
invariant(this._feed);
|
|
84
95
|
invariant(this._runtime);
|
|
85
96
|
this._binder = new AiContext.Binder({
|
|
@@ -101,11 +112,23 @@ export class Session extends Resource {
|
|
|
101
112
|
return this._binder;
|
|
102
113
|
}
|
|
103
114
|
|
|
115
|
+
/**
|
|
116
|
+
* The conversation as the model should see it: only the messages reachable from the feed's current
|
|
117
|
+
* head, so a soft fork's abandoned turns are excluded.
|
|
118
|
+
*/
|
|
104
119
|
public async getHistory(): Promise<Message.Message[]> {
|
|
120
|
+
const { items: reachable } = Feed.history(await this.#messagesInAppendOrder());
|
|
121
|
+
return Runtime.runPromise(this._runtime)(this._sessionLoader.reifyHistory(this._feed, reachable));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Every message in the feed, in append order — what `Feed.history` needs, since it walks lineage
|
|
126
|
+
* positionally rather than by `created`, a wall clock peers do not agree on.
|
|
127
|
+
*/
|
|
128
|
+
async #messagesInAppendOrder(): Promise<Message.Message[]> {
|
|
105
129
|
const queryResult = await Runtime.runPromise(this._runtime)(Feed.query(this._feed, Filter.type(Message.Message)));
|
|
106
130
|
const items = await queryResult.run();
|
|
107
|
-
|
|
108
|
-
return Runtime.runPromise(this._runtime)(this._sessionLoader.reifyHistory(this._feed, messages));
|
|
131
|
+
return Array.sort(items.filter(Obj.instanceOf(Message.Message)), byFeedPosition);
|
|
109
132
|
}
|
|
110
133
|
|
|
111
134
|
getTools(): Effect.Effect<
|
|
@@ -132,6 +155,39 @@ export class Session extends Resource {
|
|
|
132
155
|
);
|
|
133
156
|
}
|
|
134
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Appends one of this turn's messages to the feed, consuming a pending rewind if there is one.
|
|
160
|
+
*
|
|
161
|
+
* The rewind decision is made in the UI, but the continuation is appended by the agent's process,
|
|
162
|
+
* which resolves the feed and never sees the chat — so the feed carries the intent and this is where
|
|
163
|
+
* it becomes lineage. `rewindFrom` names the earliest discarded message, so the new parent is whatever
|
|
164
|
+
* precedes it; nothing preceding means the continuation starts a fresh line. Only the first message of
|
|
165
|
+
* a turn finds it set, since the append clears it, so the rest of the turn chains implicitly. Clearing
|
|
166
|
+
* after the append (rather than before) leaves the rewind pending if the turn fails.
|
|
167
|
+
*/
|
|
168
|
+
public async appendTurnMessage(message: Message.Message): Promise<void> {
|
|
169
|
+
const rewindFrom = this._feed.rewindFrom;
|
|
170
|
+
const parent = rewindFrom !== undefined ? await this.#parentForRewind(rewindFrom) : undefined;
|
|
171
|
+
|
|
172
|
+
return Runtime.runPromise(this._runtime)(
|
|
173
|
+
Effect.gen(this, function* () {
|
|
174
|
+
yield* Feed.append(this._feed, [message], parent !== undefined ? { parent } : undefined);
|
|
175
|
+
if (rewindFrom !== undefined) {
|
|
176
|
+
Obj.update(this._feed, (feed) => {
|
|
177
|
+
feed.rewindFrom = undefined;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** The message preceding `rewindFrom` in append order, which the continuation parents to. */
|
|
185
|
+
async #parentForRewind(rewindFrom: string): Promise<string | undefined> {
|
|
186
|
+
const messages = await this.#messagesInAppendOrder();
|
|
187
|
+
const index = messages.findIndex((message) => message.id === rewindFrom);
|
|
188
|
+
return index > 0 ? messages[index - 1].id : undefined;
|
|
189
|
+
}
|
|
190
|
+
|
|
135
191
|
/**
|
|
136
192
|
* Creates a new cancelable request effect.
|
|
137
193
|
*/
|
|
@@ -153,14 +209,14 @@ export class Session extends Resource {
|
|
|
153
209
|
summarizationThreshold: SUMMARY_THRESHOLD,
|
|
154
210
|
observer: params.observer,
|
|
155
211
|
persist: params.persist,
|
|
156
|
-
onOutput: (message) =>
|
|
157
|
-
Effect.promise(() => Runtime.runPromise(this._runtime)(Feed.append(this._feed, [message]))),
|
|
212
|
+
onOutput: (message) => Effect.promise(() => this.appendTurnMessage(message)),
|
|
158
213
|
});
|
|
159
214
|
|
|
160
215
|
yield* request.begin({
|
|
161
216
|
history,
|
|
162
217
|
skills,
|
|
163
218
|
objects,
|
|
219
|
+
instructions: this.#instructions,
|
|
164
220
|
prompt: params.prompt,
|
|
165
221
|
system: params.system,
|
|
166
222
|
});
|
|
@@ -189,6 +245,7 @@ export class Session extends Resource {
|
|
|
189
245
|
system: params.system,
|
|
190
246
|
skills: currentSkills,
|
|
191
247
|
objects: this.context.getObjects(),
|
|
248
|
+
instructions: this.#instructions,
|
|
192
249
|
}).pipe(Effect.orDie);
|
|
193
250
|
|
|
194
251
|
const { done, finishReason } = yield* request.runAgentTurn({ system, toolkit });
|
|
@@ -294,3 +351,19 @@ const connectMcpServers = (
|
|
|
294
351
|
Effect.map(Array.filterMap((_) => Either.getRight(_))),
|
|
295
352
|
);
|
|
296
353
|
};
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Orders feed items by the position the server assigned them. Unpositioned blocks (written locally and
|
|
357
|
+
* not yet acknowledged) sort last, which is what we want: a message just written is the newest.
|
|
358
|
+
*/
|
|
359
|
+
const byFeedPosition = Order.make<Message.Message>((a, b) => {
|
|
360
|
+
const positionA = feedPosition(a);
|
|
361
|
+
const positionB = feedPosition(b);
|
|
362
|
+
return positionA === positionB ? 0 : positionA < positionB ? -1 : 1;
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const feedPosition = (message: Message.Message): number => {
|
|
366
|
+
const key = Obj.getKeys(message, FeedProtocol.KEY_QUEUE_POSITION).at(0)?.id;
|
|
367
|
+
const position = key !== undefined ? Number(key) : Number.NaN;
|
|
368
|
+
return Number.isNaN(position) ? Number.POSITIVE_INFINITY : position;
|
|
369
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
import { describe, test } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { Obj, Ref } from '@dxos/echo';
|
|
9
|
+
import { EID, EntityId, SpaceId } from '@dxos/keys';
|
|
10
|
+
|
|
11
|
+
import { createStructFieldsFromSchema } from './services';
|
|
12
|
+
|
|
13
|
+
describe('createStructFieldsFromSchema', () => {
|
|
14
|
+
const SPACE = SpaceId.random();
|
|
15
|
+
const OBJECT = EntityId.random();
|
|
16
|
+
|
|
17
|
+
// Projects a tool input schema for the LLM and decodes the given `in` value the way a tool call would.
|
|
18
|
+
const decodeIn = (schema: Schema.Schema<any, any>, value: unknown): any[] => {
|
|
19
|
+
const fields = createStructFieldsFromSchema(schema);
|
|
20
|
+
const decoded: any = Schema.decodeUnknownSync(Schema.Struct(fields))({ in: value });
|
|
21
|
+
return decoded.in;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// An LLM-supplied ref URI string must coerce to a valid local EID. Refs nested inside an optional
|
|
25
|
+
// field surface as a `T | undefined` union; the projection must still route them through the
|
|
26
|
+
// LLM-friendly coercion so a qualified URI does not become a malformed `echo:////…`.
|
|
27
|
+
test('coerces refs passed as qualified URI strings inside an optional array', ({ expect }) => {
|
|
28
|
+
const refs = decodeIn(Schema.Struct({ in: Schema.optional(Schema.Array(Ref.Ref(Obj.Unknown))) }), [
|
|
29
|
+
`echo://${SPACE}/${OBJECT}`,
|
|
30
|
+
]);
|
|
31
|
+
expect(refs).toHaveLength(1);
|
|
32
|
+
expect(() => EID.parse(refs[0].uri)).not.toThrow();
|
|
33
|
+
expect(refs[0].uri).toBe(`echo:///${OBJECT}`);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('coerces refs passed as qualified URI strings inside a required array', ({ expect }) => {
|
|
37
|
+
const refs = decodeIn(Schema.Struct({ in: Schema.Array(Ref.Ref(Obj.Unknown)) }), [`echo://${SPACE}/${OBJECT}`]);
|
|
38
|
+
expect(refs).toHaveLength(1);
|
|
39
|
+
expect(refs[0].uri).toBe(`echo:///${OBJECT}`);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -173,7 +173,14 @@ const makeToolName = (name: string) => {
|
|
|
173
173
|
};
|
|
174
174
|
|
|
175
175
|
// TODO(dmaretskyi): Factor out.
|
|
176
|
-
|
|
176
|
+
/**
|
|
177
|
+
* Projects an operation input struct into the LLM-facing tool parameter fields, mapping each
|
|
178
|
+
* property through {@link mapSchemaTypeForLLM} (so refs become the model-friendly `RefFromLLM`).
|
|
179
|
+
* Exported for testing.
|
|
180
|
+
*/
|
|
181
|
+
export const createStructFieldsFromSchema = (
|
|
182
|
+
schema: Schema.Schema<any, any>,
|
|
183
|
+
): Record<string, Schema.Schema<any, any>> => {
|
|
177
184
|
switch (schema.ast._tag) {
|
|
178
185
|
case 'TypeLiteral':
|
|
179
186
|
return Object.fromEntries(
|
|
@@ -218,6 +225,14 @@ const mapSchemaTypeForLLM = (ast: SchemaAST.AST): SchemaAST.AST => {
|
|
|
218
225
|
ast.indexSignatures,
|
|
219
226
|
ast.annotations,
|
|
220
227
|
);
|
|
228
|
+
} else if (SchemaAST.isUnion(ast)) {
|
|
229
|
+
// `Schema.optional(Schema.Array(Ref(...)))` surfaces the value as a `T | undefined` union, so a
|
|
230
|
+
// ref nested inside an optional/union field must be mapped through each member — otherwise it
|
|
231
|
+
// keeps the raw `Ref` encoding and an LLM-supplied URI string is never coerced via `RefFromLLM`.
|
|
232
|
+
return SchemaAST.Union.make(
|
|
233
|
+
ast.types.map((member) => mapSchemaTypeForLLM(member)),
|
|
234
|
+
ast.annotations,
|
|
235
|
+
);
|
|
221
236
|
}
|
|
222
237
|
|
|
223
238
|
return ast;
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { AgentStatus } from '@dxos/ai';
|
|
6
|
+
import { MESSAGE_PROPERTY_TOOL_CALL_ID } from '@dxos/compute-runtime';
|
|
6
7
|
import { type Entity, Obj, type Ref } from '@dxos/echo';
|
|
7
|
-
import { MESSAGE_PROPERTY_TOOL_CALL_ID } from '@dxos/functions';
|
|
8
8
|
import { EID, type EntityId } from '@dxos/keys';
|
|
9
9
|
import { LogLevel } from '@dxos/log';
|
|
10
10
|
import { ContentBlock, Message } from '@dxos/types';
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import "../chunk-J5LGTIGS.mjs";
|
|
2
|
-
|
|
3
|
-
// src/extraction/extraction-llm-function/proper-noun-extraction.ts
|
|
4
|
-
import * as LanguageModel from "@effect/ai/LanguageModel";
|
|
5
|
-
import * as Effect from "effect/Effect";
|
|
6
|
-
import * as Schema2 from "effect/Schema";
|
|
7
|
-
import { AiService } from "@dxos/ai";
|
|
8
|
-
import { Obj } from "@dxos/echo";
|
|
9
|
-
import { Message } from "@dxos/types";
|
|
10
|
-
|
|
11
|
-
// src/extraction/quotes.ts
|
|
12
|
-
import * as Schema from "effect/Schema";
|
|
13
|
-
import { Filter, Query } from "@dxos/echo";
|
|
14
|
-
import { EID, EntityId } from "@dxos/keys";
|
|
15
|
-
var ReferencedQuotes = Schema.Struct({
|
|
16
|
-
references: Schema.Array(Schema.Struct({
|
|
17
|
-
id: Schema.String,
|
|
18
|
-
quote: Schema.String
|
|
19
|
-
})).pipe(Schema.mutable).annotations({
|
|
20
|
-
// TODO(burdon): Does this description make sense?
|
|
21
|
-
description: `
|
|
22
|
-
The references to the context objects that are mentioned in the transcript.
|
|
23
|
-
Quote should match the original transcript text exactly, while id is the id of the context object.
|
|
24
|
-
`
|
|
25
|
-
})
|
|
26
|
-
});
|
|
27
|
-
var findReferences = async (nouns, db, { searchKind = "full-text" } = {}) => {
|
|
28
|
-
const normalizedNouns = Array.from(new Set(nouns.map((noun) => noun.trim()).filter((noun) => noun.length > 0))).sort((leftNoun, rightNoun) => rightNoun.length - leftNoun.length);
|
|
29
|
-
const references = await Promise.all(normalizedNouns.map(async (noun) => {
|
|
30
|
-
const objects = await db.query(Query.select(Filter.text(noun, {
|
|
31
|
-
type: searchKind
|
|
32
|
-
}))).run();
|
|
33
|
-
return objects.length > 0 ? [
|
|
34
|
-
{
|
|
35
|
-
id: objects[0].id.toString(),
|
|
36
|
-
quote: noun
|
|
37
|
-
}
|
|
38
|
-
] : [];
|
|
39
|
-
}));
|
|
40
|
-
return {
|
|
41
|
-
references: references.flat()
|
|
42
|
-
};
|
|
43
|
-
};
|
|
44
|
-
var insertReferences = (text, quotes) => {
|
|
45
|
-
const references = quotes.references.filter((quote) => EntityId.isValid(quote.id)).sort((left, right) => right.quote.length - left.quote.length);
|
|
46
|
-
for (const quote of references) {
|
|
47
|
-
const regex = new RegExp(quote.quote.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
|
|
48
|
-
text = replaceOutsideLinks(text, regex, `[${quote.quote}](${EID.make({
|
|
49
|
-
entityId: quote.id
|
|
50
|
-
})})`);
|
|
51
|
-
}
|
|
52
|
-
return text;
|
|
53
|
-
};
|
|
54
|
-
var MARKDOWN_LINK = /\[[^\]]*\]\([^)]*\)/g;
|
|
55
|
-
var replaceOutsideLinks = (text, regex, replacement) => {
|
|
56
|
-
let result = "";
|
|
57
|
-
let plainStart = 0;
|
|
58
|
-
for (const link of text.matchAll(MARKDOWN_LINK)) {
|
|
59
|
-
const index = link.index ?? 0;
|
|
60
|
-
result += text.slice(plainStart, index).replace(regex, replacement) + link[0];
|
|
61
|
-
plainStart = index + link[0].length;
|
|
62
|
-
}
|
|
63
|
-
return result + text.slice(plainStart).replace(regex, replacement);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// src/extraction/extraction-llm-function/proper-noun-extraction.ts
|
|
67
|
-
var EXTRACTION_MODEL = "com.anthropic.model.claude-haiku-4-5.default";
|
|
68
|
-
var ProperNouns = Schema2.Struct({
|
|
69
|
-
properNouns: Schema2.Array(Schema2.String).annotations({
|
|
70
|
-
description: "Proper nouns (names of people, organizations, places, products) mentioned in the text."
|
|
71
|
-
})
|
|
72
|
-
});
|
|
73
|
-
var extractProperNouns = (text) => Effect.gen(function* () {
|
|
74
|
-
const { value } = yield* Effect.scoped(LanguageModel.generateObject({
|
|
75
|
-
schema: ProperNouns,
|
|
76
|
-
prompt: [
|
|
77
|
-
"Extract every proper noun (people, organizations, places, products) mentioned in the transcript text below.",
|
|
78
|
-
"Return only the surface strings exactly as they appear. Exclude common nouns and pronouns.",
|
|
79
|
-
"",
|
|
80
|
-
"Transcript:",
|
|
81
|
-
text
|
|
82
|
-
].join("\n")
|
|
83
|
-
}));
|
|
84
|
-
return value.properNouns.map((noun) => noun.trim()).filter((noun) => noun.length >= 3);
|
|
85
|
-
}).pipe(Effect.provide(AiService.model(EXTRACTION_MODEL)));
|
|
86
|
-
var enrichTranscriptMessage = (message, { db }) => Effect.gen(function* () {
|
|
87
|
-
const blocks = yield* Effect.forEach(message.blocks, (block) => Effect.gen(function* () {
|
|
88
|
-
if (block._tag !== "transcript") {
|
|
89
|
-
return block;
|
|
90
|
-
}
|
|
91
|
-
const nouns = yield* extractProperNouns(block.text);
|
|
92
|
-
const references = yield* Effect.promise(() => findReferences([
|
|
93
|
-
...nouns
|
|
94
|
-
], db));
|
|
95
|
-
return {
|
|
96
|
-
...block,
|
|
97
|
-
text: insertReferences(block.text, references)
|
|
98
|
-
};
|
|
99
|
-
}));
|
|
100
|
-
return Obj.make(Message.Message, {
|
|
101
|
-
id: message.id,
|
|
102
|
-
sender: message.sender,
|
|
103
|
-
created: message.created,
|
|
104
|
-
properties: message.properties,
|
|
105
|
-
blocks
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// src/extraction/extraction-ner-function/named-entity-recognition.ts
|
|
110
|
-
import { pipeline } from "@xenova/transformers";
|
|
111
|
-
import { log } from "@dxos/log";
|
|
112
|
-
var __dxlog_file = "/__w/dxos/dxos/packages/core/compute/assistant/src/extraction/extraction-ner-function/named-entity-recognition.ts";
|
|
113
|
-
var _ner;
|
|
114
|
-
var getNer = () => {
|
|
115
|
-
if (!_ner) {
|
|
116
|
-
_ner = pipeline("ner", "Xenova/bert-base-NER").then((ner) => {
|
|
117
|
-
log.info("NER model is ready", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 14, S: void 0 });
|
|
118
|
-
return ner;
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
return _ner;
|
|
122
|
-
};
|
|
123
|
-
var extractFullEntities = async (text) => {
|
|
124
|
-
const ner = await getNer();
|
|
125
|
-
const entities = await ner(text);
|
|
126
|
-
const tokenGroups = createTokenGroups(entities);
|
|
127
|
-
const combinedEntities = tokenGroups.map(combineNerTokens);
|
|
128
|
-
return combinedEntities;
|
|
129
|
-
};
|
|
130
|
-
var createTokenGroups = (tokens) => {
|
|
131
|
-
const tokenGroups = [];
|
|
132
|
-
const canBeCombined = (prevToken, currentToken) => {
|
|
133
|
-
const [currentState, currentEntity] = currentToken.entity.split("-");
|
|
134
|
-
const [prevState, prevEntity] = prevToken.entity.split("-");
|
|
135
|
-
if (prevEntity !== currentEntity) {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
if (prevState === "O") {
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
if (currentState === "I") {
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
if ((prevState === "B" || prevState === "I") && currentState === "B") {
|
|
145
|
-
return currentToken.word.startsWith("#");
|
|
146
|
-
}
|
|
147
|
-
return false;
|
|
148
|
-
};
|
|
149
|
-
for (const token of tokens) {
|
|
150
|
-
if (tokenGroups.length === 0) {
|
|
151
|
-
tokenGroups.push([
|
|
152
|
-
token
|
|
153
|
-
]);
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
const lastTokenGroup = tokenGroups.at(-1);
|
|
157
|
-
if (canBeCombined(lastTokenGroup.at(-1), token)) {
|
|
158
|
-
lastTokenGroup.push(token);
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
tokenGroups.push([
|
|
162
|
-
token
|
|
163
|
-
]);
|
|
164
|
-
}
|
|
165
|
-
return tokenGroups;
|
|
166
|
-
};
|
|
167
|
-
var combineNerTokens = (group) => {
|
|
168
|
-
const combinedToken = {
|
|
169
|
-
entity: group.at(0).entity.split("-")[1],
|
|
170
|
-
score: group.reduce((acc, token) => acc + token.score, 0) / group.length,
|
|
171
|
-
index: group.at(0).index,
|
|
172
|
-
word: group.at(0).word,
|
|
173
|
-
start: group.at(0).start,
|
|
174
|
-
end: group.at(-1).end
|
|
175
|
-
};
|
|
176
|
-
for (const token of group.slice(1)) {
|
|
177
|
-
if (token.word.startsWith("#")) {
|
|
178
|
-
combinedToken.word += token.word.replace(/^#+/, "");
|
|
179
|
-
} else {
|
|
180
|
-
combinedToken.word += " " + token.word;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return combinedToken;
|
|
184
|
-
};
|
|
185
|
-
export {
|
|
186
|
-
ProperNouns,
|
|
187
|
-
ReferencedQuotes,
|
|
188
|
-
combineNerTokens,
|
|
189
|
-
createTokenGroups,
|
|
190
|
-
enrichTranscriptMessage,
|
|
191
|
-
extractFullEntities,
|
|
192
|
-
extractProperNouns,
|
|
193
|
-
findReferences,
|
|
194
|
-
getNer,
|
|
195
|
-
insertReferences
|
|
196
|
-
};
|
|
197
|
-
//# sourceMappingURL=index.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/extraction/extraction-llm-function/proper-noun-extraction.ts", "../../../../src/extraction/quotes.ts", "../../../../src/extraction/extraction-ner-function/named-entity-recognition.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as LanguageModel from '@effect/ai/LanguageModel';\nimport * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\n\nimport { AiService } from '@dxos/ai';\nimport { type Database, Obj } from '@dxos/echo';\nimport { Message } from '@dxos/types';\n\nimport { findReferences, insertReferences } from '../quotes';\n\nconst EXTRACTION_MODEL = 'com.anthropic.model.claude-haiku-4-5.default';\n\n/**\n * Proper nouns extracted from transcript text.\n */\nexport const ProperNouns = Schema.Struct({\n properNouns: Schema.Array(Schema.String).annotations({\n description: 'Proper nouns (names of people, organizations, places, products) mentioned in the text.',\n }),\n});\nexport type ProperNouns = Schema.Schema.Type<typeof ProperNouns>;\n\n/**\n * Extracts proper nouns from transcript text using a small LLM (Haiku). Provides the LanguageModel\n * internally so the residual requirement is just {@link AiService.AiService}.\n */\nexport const extractProperNouns = (text: string) =>\n Effect.gen(function* () {\n const { value } = yield* Effect.scoped(\n LanguageModel.generateObject({\n schema: ProperNouns,\n prompt: [\n 'Extract every proper noun (people, organizations, places, products) mentioned in the transcript text below.',\n 'Return only the surface strings exactly as they appear. Exclude common nouns and pronouns.',\n '',\n 'Transcript:',\n text,\n ].join('\\n'),\n }),\n );\n\n // Drop short tokens (e.g. \"IT\"): insertReferences replaces case-insensitive substrings, so a\n // short noun would match inside unrelated words (\"secur[IT]y\") and corrupt neighbouring links.\n return value.properNouns.map((noun) => noun.trim()).filter((noun) => noun.length >= 3);\n }).pipe(Effect.provide(AiService.model(EXTRACTION_MODEL)));\n\n/**\n * Enriches a transcript message: for each transcript block, extracts proper nouns, links them to\n * objects in the space via full-text search, and rewrites the text with `[noun](dxn)` references.\n * Requires {@link AiService.AiService}; takes the space database handle.\n */\nexport const enrichTranscriptMessage = (message: Message.Message, { db }: { db: Database.Database }) =>\n Effect.gen(function* () {\n const blocks = yield* Effect.forEach(message.blocks, (block) =>\n Effect.gen(function* () {\n if (block._tag !== 'transcript') {\n return block;\n }\n\n const nouns = yield* extractProperNouns(block.text);\n const references = yield* Effect.promise(() => findReferences([...nouns], db));\n return { ...block, text: insertReferences(block.text, references) };\n }),\n );\n\n // Preserve the message identity and metadata; enrichment only rewrites transcript block text.\n return Obj.make(Message.Message, {\n id: message.id,\n sender: message.sender,\n created: message.created,\n properties: message.properties,\n blocks,\n });\n });\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { type Database, Filter, Query } from '@dxos/echo';\nimport { EID, EntityId } from '@dxos/keys';\n\nexport const ReferencedQuotes = Schema.Struct({\n references: Schema.Array(\n Schema.Struct({\n id: Schema.String,\n quote: Schema.String, // TODO(burdon): Quote?\n }),\n )\n .pipe(Schema.mutable)\n .annotations({\n // TODO(burdon): Does this description make sense?\n description: `\n The references to the context objects that are mentioned in the transcript. \n Quote should match the original transcript text exactly, while id is the id of the context object.\n `,\n }),\n});\nexport type ReferencedQuotes = Schema.Schema.Type<typeof ReferencedQuotes>;\n\nexport type FindReferencesOptions = {\n /** Search backend. Full-text matches proper nouns precisely; vector matches semantically. */\n searchKind?: 'full-text' | 'vector';\n};\n\n/**\n * Finds the best-matching context object for each noun and returns it as a reference.\n */\nexport const findReferences = async (\n nouns: string[],\n db: Database.Database,\n { searchKind = 'full-text' }: FindReferencesOptions = {},\n): Promise<ReferencedQuotes> => {\n // Deduplicate and process longer nouns first: `insertReferences` rewrites case-insensitive\n // substrings, so a repeated or shorter overlapping noun would re-replace already-linked text and\n // produce nested/broken markdown links.\n const normalizedNouns = Array.from(new Set(nouns.map((noun) => noun.trim()).filter((noun) => noun.length > 0))).sort(\n (leftNoun, rightNoun) => rightNoun.length - leftNoun.length,\n );\n\n const references = await Promise.all(\n normalizedNouns.map(async (noun) => {\n const objects = await db.query(Query.select(Filter.text(noun, { type: searchKind }))).run();\n return objects.length > 0 ? [{ id: objects[0].id.toString(), quote: noun }] : [];\n }),\n );\n return { references: references.flat() };\n};\n\n/**\n * Finds and replaces all quotes with DXNs references.\n */\n// TODO(dmaretskyi): Lookup and verifiy ids from provided context.\nexport const insertReferences = (text: string, quotes: ReferencedQuotes): string => {\n // Process longer quotes first so e.g. \"Sarah Johnson\" is linked before \"Sarah\". Combined with the\n // link-aware replacement below this stops a shorter or overlapping quote from rewriting text that\n // is already part of an inserted link (which would produce nested `[[..](..)](..)` markdown).\n const references = quotes.references\n .filter((quote) => EntityId.isValid(quote.id))\n .sort((left, right) => right.quote.length - left.quote.length);\n\n for (const quote of references) {\n // Case-insensitive match of the (regex-escaped) quote.\n const regex = new RegExp(quote.quote.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'gi');\n text = replaceOutsideLinks(text, regex, `[${quote.quote}](${EID.make({ entityId: quote.id })})`);\n }\n\n return text;\n};\n\nconst MARKDOWN_LINK = /\\[[^\\]]*\\]\\([^)]*\\)/g;\n\n/** Applies a replacement to every span of `text` that is not already inside a `[label](url)` link. */\nconst replaceOutsideLinks = (text: string, regex: RegExp, replacement: string): string => {\n let result = '';\n let plainStart = 0;\n for (const link of text.matchAll(MARKDOWN_LINK)) {\n const index = link.index ?? 0;\n result += text.slice(plainStart, index).replace(regex, replacement) + link[0];\n plainStart = index + link[0].length;\n }\n return result + text.slice(plainStart).replace(regex, replacement);\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport {\n type TokenClassificationOutput,\n type TokenClassificationPipelineType,\n type TokenClassificationSingle,\n pipeline,\n} from '@xenova/transformers';\n\nimport { log } from '@dxos/log';\n\nlet _ner: Promise<TokenClassificationPipelineType>;\n\n/**\n * Named Entity Recognition pipeline.\n * Initializes the pipeline on first call.\n * @returns The singleton promise that resolves to a token classification pipeline.\n */\nexport const getNer = () => {\n if (!_ner) {\n _ner = pipeline('ner', 'Xenova/bert-base-NER').then((ner) => {\n log.info('NER model is ready');\n return ner;\n });\n }\n\n return _ner;\n};\n\n/**\n * Extracts entities from text using the named entity recognition pipeline and does simple grouping of tokens.\n */\nexport const extractFullEntities = async (text: string): Promise<TokenClassificationSingle[]> => {\n const ner = await getNer();\n const entities = (await ner(text)) as TokenClassificationOutput;\n const tokenGroups = createTokenGroups(entities);\n const combinedEntities = tokenGroups.map(combineNerTokens);\n return combinedEntities;\n};\n\nexport const createTokenGroups = (tokens: TokenClassificationSingle[]): TokenClassificationSingle[][] => {\n const tokenGroups: TokenClassificationSingle[][] = [];\n\n const canBeCombined = (prevToken: TokenClassificationSingle, currentToken: TokenClassificationSingle) => {\n const [currentState, currentEntity] = currentToken.entity.split('-'); // B, I, O\n const [prevState, prevEntity] = prevToken.entity.split('-'); // B, I, O\n if (prevEntity !== currentEntity) {\n return false;\n }\n if (prevState === 'O') {\n return false;\n }\n if (currentState === 'I') {\n return true;\n }\n if ((prevState === 'B' || prevState === 'I') && currentState === 'B') {\n return currentToken.word.startsWith('#');\n }\n return false;\n };\n\n for (const token of tokens) {\n if (tokenGroups.length === 0) {\n tokenGroups.push([token]);\n continue;\n }\n const lastTokenGroup = tokenGroups.at(-1)!;\n if (canBeCombined(lastTokenGroup.at(-1)!, token)) {\n lastTokenGroup.push(token);\n continue;\n }\n tokenGroups.push([token]);\n }\n\n return tokenGroups;\n};\n\nexport const combineNerTokens = (group: TokenClassificationSingle[]): TokenClassificationSingle => {\n const combinedToken: TokenClassificationSingle = {\n entity: group.at(0)!.entity.split('-')[1],\n score: group.reduce((acc, token) => acc + token.score, 0) / group.length,\n index: group.at(0)!.index,\n word: group.at(0)!.word,\n start: group.at(0)!.start,\n end: group.at(-1)!.end,\n };\n\n for (const token of group.slice(1)) {\n if (token.word.startsWith('#')) {\n combinedToken.word += token.word.replace(/^#+/, '');\n } else {\n combinedToken.word += ' ' + token.word;\n }\n }\n\n return combinedToken;\n};\n"],
|
|
5
|
-
"mappings": ";;;AAIA,YAAYA,mBAAmB;AAC/B,YAAYC,YAAY;AACxB,YAAYC,aAAY;AAExB,SAASC,iBAAiB;AAC1B,SAAwBC,WAAW;AACnC,SAASC,eAAe;;;ACNxB,YAAYC,YAAY;AAExB,SAAwBC,QAAQC,aAAa;AAC7C,SAASC,KAAKC,gBAAgB;AAEvB,IAAMC,mBAA0BC,cAAO;EAC5CC,YAAmBC,aACVF,cAAO;IACZG,IAAWC;IACXC,OAAcD;EAChB,CAAA,CAAA,EAECE,KAAYC,cAAO,EACnBC,YAAY;;IAEXC,aAAa;;;;EAIf,CAAA;AACJ,CAAA;AAWO,IAAMC,iBAAiB,OAC5BC,OACAC,IACA,EAAEC,aAAa,YAAW,IAA4B,CAAC,MAAC;AAKxD,QAAMC,kBAAkBZ,MAAMa,KAAK,IAAIC,IAAIL,MAAMM,IAAI,CAACC,SAASA,KAAKC,KAAI,CAAA,EAAIC,OAAO,CAACF,SAASA,KAAKG,SAAS,CAAA,CAAA,CAAA,EAAKC,KAC9G,CAACC,UAAUC,cAAcA,UAAUH,SAASE,SAASF,MAAM;AAG7D,QAAMpB,aAAa,MAAMwB,QAAQC,IAC/BZ,gBAAgBG,IAAI,OAAOC,SAAAA;AACzB,UAAMS,UAAU,MAAMf,GAAGgB,MAAMhC,MAAMiC,OAAOlC,OAAOmC,KAAKZ,MAAM;MAAEa,MAAMlB;IAAW,CAAA,CAAA,CAAA,EAAKmB,IAAG;AACzF,WAAOL,QAAQN,SAAS,IAAI;MAAC;QAAElB,IAAIwB,QAAQ,CAAA,EAAGxB,GAAG8B,SAAQ;QAAI5B,OAAOa;MAAK;QAAK,CAAA;EAChF,CAAA,CAAA;AAEF,SAAO;IAAEjB,YAAYA,WAAWiC,KAAI;EAAG;AACzC;AAMO,IAAMC,mBAAmB,CAACL,MAAcM,WAAAA;AAI7C,QAAMnC,aAAamC,OAAOnC,WACvBmB,OAAO,CAACf,UAAUP,SAASuC,QAAQhC,MAAMF,EAAE,CAAA,EAC3CmB,KAAK,CAACgB,MAAMC,UAAUA,MAAMlC,MAAMgB,SAASiB,KAAKjC,MAAMgB,MAAM;AAE/D,aAAWhB,SAASJ,YAAY;AAE9B,UAAMuC,QAAQ,IAAIC,OAAOpC,MAAMA,MAAMqC,QAAQ,uBAAuB,MAAA,GAAS,IAAA;AAC7EZ,WAAOa,oBAAoBb,MAAMU,OAAO,IAAInC,MAAMA,KAAK,KAAKR,IAAI+C,KAAK;MAAEC,UAAUxC,MAAMF;IAAG,CAAA,CAAA,GAAK;EACjG;AAEA,SAAO2B;AACT;AAEA,IAAMgB,gBAAgB;AAGtB,IAAMH,sBAAsB,CAACb,MAAcU,OAAeO,gBAAAA;AACxD,MAAIC,SAAS;AACb,MAAIC,aAAa;AACjB,aAAWC,QAAQpB,KAAKqB,SAASL,aAAAA,GAAgB;AAC/C,UAAMM,QAAQF,KAAKE,SAAS;AAC5BJ,cAAUlB,KAAKuB,MAAMJ,YAAYG,KAAAA,EAAOV,QAAQF,OAAOO,WAAAA,IAAeG,KAAK,CAAA;AAC3ED,iBAAaG,QAAQF,KAAK,CAAA,EAAG7B;EAC/B;AACA,SAAO2B,SAASlB,KAAKuB,MAAMJ,UAAAA,EAAYP,QAAQF,OAAOO,WAAAA;AACxD;;;AD3EA,IAAMO,mBAAmB;AAKlB,IAAMC,cAAqBC,eAAO;EACvCC,aAAoBC,cAAaC,cAAM,EAAEC,YAAY;IACnDC,aAAa;EACf,CAAA;AACF,CAAA;AAOO,IAAMC,qBAAqB,CAACC,SAC1BC,WAAI,aAAA;AACT,QAAM,EAAEC,MAAK,IAAK,OAAcC,cAChBC,6BAAe;IAC3BC,QAAQb;IACRc,QAAQ;MACN;MACA;MACA;MACA;MACAN;MACAO,KAAK,IAAA;EACT,CAAA,CAAA;AAKF,SAAOL,MAAMR,YAAYc,IAAI,CAACC,SAASA,KAAKC,KAAI,CAAA,EAAIC,OAAO,CAACF,SAASA,KAAKG,UAAU,CAAA;AACtF,CAAA,EAAGC,KAAYC,eAAQC,UAAUC,MAAMzB,gBAAAA,CAAAA,CAAAA;AAOlC,IAAM0B,0BAA0B,CAACC,SAA0B,EAAEC,GAAE,MAC7DlB,WAAI,aAAA;AACT,QAAMmB,SAAS,OAAcC,eAAQH,QAAQE,QAAQ,CAACE,UAC7CrB,WAAI,aAAA;AACT,QAAIqB,MAAMC,SAAS,cAAc;AAC/B,aAAOD;IACT;AAEA,UAAME,QAAQ,OAAOzB,mBAAmBuB,MAAMtB,IAAI;AAClD,UAAMyB,aAAa,OAAcC,eAAQ,MAAMC,eAAe;SAAIH;OAAQL,EAAAA,CAAAA;AAC1E,WAAO;MAAE,GAAGG;MAAOtB,MAAM4B,iBAAiBN,MAAMtB,MAAMyB,UAAAA;IAAY;EACpE,CAAA,CAAA;AAIF,SAAOI,IAAIC,KAAKC,QAAQA,SAAS;IAC/BC,IAAId,QAAQc;IACZC,QAAQf,QAAQe;IAChBC,SAAShB,QAAQgB;IACjBC,YAAYjB,QAAQiB;IACpBf;EACF,CAAA;AACF,CAAA;;;AEzEF,SAIEgB,gBACK;AAEP,SAASC,WAAW;AAEpB,IAAIC,eAAAA;;AASAA,IAAOF,SAAS,MAAA;aACdC;WACA,SAAOE,OAAAA,sBAAAA,EAAAA,KAAAA,CAAAA,QAAAA;AACT,UAAA,KAAA,sBAAA,QAAA,EAAA,YAAA,YAAA,GAAA,cAAA,GAAA,IAAA,GAAA,OAAA,CAAA;AACF,aAAA;IAEA,CAAA;EACA;AAEF,SAAA;;AAKQC,IAAAA,sBAAsBC,OAAAA,SAAAA;AAC5B,QAAMC,MAAAA,MAAAA,OAAcC;AACpB,QAAMC,WAAAA,MAAAA,IAAmBF,IAAAA;AACzB,QAAA,cAAOE,kBAAAA,QAAAA;AACP,QAAA,mBAAA,YAAA,IAAA,gBAAA;AAEF,SAAO;;AAGL,IAAMC,oBAAiBC,CAAAA,WAAsCC;QAC3D,cAAOC,CAAAA;QACP,gBAAkBC,CAAAA,WAAW,iBAAaC;AAC1C,UAAID,CAAAA,cAAeE,aAAe,IAAA,aAAA,OAAA,MAAA,GAAA;UAChC,CAAA,WAAO,UAAA,IAAA,UAAA,OAAA,MAAA,GAAA;AACT,QAAA,eAAA,eAAA;AACIC,aAAAA;;AAEJ,QAAA,cAAA,KAAA;AACIJ,aAAAA;;AAEJ,QAAA,iBAAA,KAAA;AACKI,aAAAA;;AAEL,SAAA,cAAA,OAAA,cAAA,QAAA,iBAAA,KAAA;AACA,aAAO,aAAA,KAAA,WAAA,GAAA;IACT;AAEA,WAAWC;;aAEPX,SAAYY,QAAK;oBAACD,WAAAA,GAAAA;kBAAM,KAAA;QACxB;MACF,CAAA;AACA;IACA;UACEE,iBAAmB,YAACF,GAAAA,EAAAA;QACpB,cAAA,eAAA,GAAA,EAAA,GAAA,KAAA,GAAA;AACF,qBAAA,KAAA,KAAA;AACAX;;gBAAwB,KAAA;MAC1B;IAEA,CAAA;EACA;AAEF,SAAO;;IAEHQ,mBAAqBA,CAAAA,UAAa;QAClCM,gBAAaC;IACbC,QAAOC,MAAMC,GAAG,CAAA,EAAIF,OAAK,MAAA,GAAA,EAAA,CAAA;IACzBG,OAAMF,MAAMC,OAAOC,CAAAA,KAAI,UAAA,MAAA,MAAA,OAAA,CAAA,IAAA,MAAA;IACvBC,OAAOH,MAAMC,GAAG,CAAA,EAAIE;IACpBC,MAAKJ,MAAMC,GAAI,CAAA,EAAIG;IACrB,OAAA,MAAA,GAAA,CAAA,EAAA;IAEA,KAAK,MAAMV,GAAAA,EAASM,EAAAA;;aAEhBK,SAAAA,MAAkB,MAAIX,CAAAA,GAAAA;AACxB,QAAA,MAAO,KAAA,WAAA,GAAA,GAAA;AACLW,oBAAcH,QAAQ,MAAMR,KAAAA,QAAU,OAAA,EAAA;IACxC,OAAA;AACF,oBAAA,QAAA,MAAA,MAAA;IAEA;EACA;;;",
|
|
6
|
-
"names": ["LanguageModel", "Effect", "Schema", "AiService", "Obj", "Message", "Schema", "Filter", "Query", "EID", "EntityId", "ReferencedQuotes", "Struct", "references", "Array", "id", "String", "quote", "pipe", "mutable", "annotations", "description", "findReferences", "nouns", "db", "searchKind", "normalizedNouns", "from", "Set", "map", "noun", "trim", "filter", "length", "sort", "leftNoun", "rightNoun", "Promise", "all", "objects", "query", "select", "text", "type", "run", "toString", "flat", "insertReferences", "quotes", "isValid", "left", "right", "regex", "RegExp", "replace", "replaceOutsideLinks", "make", "entityId", "MARKDOWN_LINK", "replacement", "result", "plainStart", "link", "matchAll", "index", "slice", "EXTRACTION_MODEL", "ProperNouns", "Struct", "properNouns", "Array", "String", "annotations", "description", "extractProperNouns", "text", "gen", "value", "scoped", "generateObject", "schema", "prompt", "join", "map", "noun", "trim", "filter", "length", "pipe", "provide", "AiService", "model", "enrichTranscriptMessage", "message", "db", "blocks", "forEach", "block", "_tag", "nouns", "references", "promise", "findReferences", "insertReferences", "Obj", "make", "Message", "id", "sender", "created", "properties", "pipeline", "log", "_ner", "ner", "entities", "text", "tokenGroups", "createTokenGroups", "combinedEntities", "canBeCombined", "prevToken", "currentToken", "currentState", "prevEntity", "entity", "currentEntity", "prevState", "token", "push", "lastTokenGroup", "score", "reduce", "index", "group", "at", "word", "start", "end", "combinedToken"]
|
|
7
|
-
}
|