@uselemma/tracing 2.12.1 → 2.16.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 +6 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/trace-wrapper.d.ts +15 -6
- package/dist/trace-wrapper.d.ts.map +1 -1
- package/dist/trace-wrapper.js +34 -10
- package/dist/trace-wrapper.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,20 +34,24 @@ enableExperimentMode();
|
|
|
34
34
|
- `ai.agent.name`
|
|
35
35
|
- `lemma.run_id`
|
|
36
36
|
- `lemma.is_experiment`
|
|
37
|
+
- `lemma.thread_id` (when `threadId` is passed at invocation time)
|
|
37
38
|
|
|
38
39
|
```typescript
|
|
39
40
|
import { wrapAgent } from "@uselemma/tracing";
|
|
40
41
|
|
|
41
42
|
const wrappedFn = wrapAgent(
|
|
42
43
|
"my-agent",
|
|
43
|
-
async ({ onComplete }) => {
|
|
44
|
+
async ({ onComplete }, { userMessage }: { userMessage: string }) => {
|
|
44
45
|
const result = await doWork(userMessage);
|
|
45
46
|
onComplete(result);
|
|
46
47
|
return result;
|
|
47
48
|
},
|
|
48
49
|
);
|
|
49
50
|
|
|
50
|
-
const { result, runId } = await wrappedFn(
|
|
51
|
+
const { result, runId } = await wrappedFn(
|
|
52
|
+
{ userMessage },
|
|
53
|
+
{ threadId: "thread_123", isExperiment: false },
|
|
54
|
+
);
|
|
51
55
|
```
|
|
52
56
|
|
|
53
57
|
## Export Behavior
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { registerOTel, createLemmaSpanProcessor, type LemmaOTelOptions, type RegisterOTelOptions, type CreateLemmaSpanProcessorOptions, } from "./register";
|
|
2
2
|
export { enableExperimentMode, disableExperimentMode, isExperimentModeEnabled, } from "./experiment-mode";
|
|
3
3
|
export { enableDebugMode, disableDebugMode, isDebugModeEnabled, } from "./debug-mode";
|
|
4
|
-
export { wrapAgent, type TraceContext, type WrapAgentOptions } from "./trace-wrapper";
|
|
4
|
+
export { wrapAgent, type TraceContext, type WrapAgentOptions, type WrapRunOptions, } from "./trace-wrapper";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,+BAA+B,GACrC,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,+BAA+B,GACrC,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,SAAS,EACT,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uCAMoB;AALlB,wGAAA,YAAY,OAAA;AACZ,oHAAA,wBAAwB,OAAA;AAK1B,qDAI2B;AAHzB,uHAAA,oBAAoB,OAAA;AACpB,wHAAA,qBAAqB,OAAA;AACrB,0HAAA,uBAAuB,OAAA;AAEzB,2CAIsB;AAHpB,6GAAA,eAAe,OAAA;AACf,8GAAA,gBAAgB,OAAA;AAChB,gHAAA,kBAAkB,OAAA;AAEpB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uCAMoB;AALlB,wGAAA,YAAY,OAAA;AACZ,oHAAA,wBAAwB,OAAA;AAK1B,qDAI2B;AAHzB,uHAAA,oBAAoB,OAAA;AACpB,wHAAA,qBAAqB,OAAA;AACrB,0HAAA,uBAAuB,OAAA;AAEzB,2CAIsB;AAHpB,6GAAA,eAAe,OAAA;AACf,8GAAA,gBAAgB,OAAA;AAChB,gHAAA,kBAAkB,OAAA;AAEpB,iDAKyB;AAJvB,0GAAA,SAAS,OAAA"}
|
package/dist/trace-wrapper.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ export type TraceContext = {
|
|
|
5
5
|
/** Unique identifier for this agent run. */
|
|
6
6
|
runId: string;
|
|
7
7
|
/**
|
|
8
|
-
* Record the run output. Sets `ai.agent.output` on
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Record the run output and end the agent span. Sets `ai.agent.output` on
|
|
9
|
+
* the span and calls `span.end()` immediately — the parent span does not
|
|
10
|
+
* stay open until the wrapped function returns. If omitted, the function's
|
|
11
|
+
* return value is captured when the wrapped function completes.
|
|
11
12
|
*/
|
|
12
13
|
onComplete: (result: unknown) => void;
|
|
13
14
|
/** Record an error on the span. Marks the span as errored. */
|
|
@@ -17,6 +18,12 @@ export type WrapAgentOptions = {
|
|
|
17
18
|
/** Mark this run as an experiment in Lemma. */
|
|
18
19
|
isExperiment?: boolean;
|
|
19
20
|
};
|
|
21
|
+
export type WrapRunOptions = {
|
|
22
|
+
/** Optional thread identifier for this invocation. */
|
|
23
|
+
threadId?: string;
|
|
24
|
+
/** Mark this specific invocation as an experiment. */
|
|
25
|
+
isExperiment?: boolean;
|
|
26
|
+
};
|
|
20
27
|
/**
|
|
21
28
|
* Wraps an agent function with OpenTelemetry tracing, automatically creating
|
|
22
29
|
* a span for the agent run and providing a `TraceContext` to the wrapped function.
|
|
@@ -25,8 +32,10 @@ export type WrapAgentOptions = {
|
|
|
25
32
|
* agent metadata (name, run ID, experiment flag), and handles error recording.
|
|
26
33
|
*
|
|
27
34
|
* `ai.agent.input` and `ai.agent.output` are set as JSON strings for Lemma
|
|
28
|
-
* ingestion and UI.
|
|
29
|
-
*
|
|
35
|
+
* ingestion and UI. When you call {@link TraceContext.onComplete}, the span
|
|
36
|
+
* ends immediately at that call — it does not wait for the wrapped function
|
|
37
|
+
* to return. If you never call `onComplete`, the span ends when the wrapped
|
|
38
|
+
* function returns or throws, and the return value is used as output.
|
|
30
39
|
*
|
|
31
40
|
* @example
|
|
32
41
|
* const myAgent = wrapAgent<{ topic: string }>(
|
|
@@ -44,7 +53,7 @@ export type WrapAgentOptions = {
|
|
|
44
53
|
* @param options.isExperiment - Mark this run as an experiment in Lemma.
|
|
45
54
|
* @returns An async function that accepts an `input`, executes `fn` inside a traced context, and returns `{ result, runId, span }`.
|
|
46
55
|
*/
|
|
47
|
-
export declare function wrapAgent<Input = unknown>(agentName: string, fn: (traceContext: TraceContext, input: Input) => any, options?: WrapAgentOptions): (this: any, input: Input) => Promise<{
|
|
56
|
+
export declare function wrapAgent<Input = unknown>(agentName: string, fn: (traceContext: TraceContext, input: Input) => any, options?: WrapAgentOptions): (this: any, input: Input, runOptions?: WrapRunOptions) => Promise<{
|
|
48
57
|
result: any;
|
|
49
58
|
runId: string;
|
|
50
59
|
span: Span;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-wrapper.d.ts","sourceRoot":"","sources":["../src/trace-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAKxE,MAAM,MAAM,YAAY,GAAG;IACzB,wDAAwD;IACxD,IAAI,EAAE,IAAI,CAAC;IACX,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd
|
|
1
|
+
{"version":3,"file":"trace-wrapper.d.ts","sourceRoot":"","sources":["../src/trace-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAKxE,MAAM,MAAM,YAAY,GAAG;IACzB,wDAAwD;IACxD,IAAI,EAAE,IAAI,CAAC;IACX,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,8DAA8D;IAC9D,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+CAA+C;IAC/C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAkBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,SAAS,CAAC,KAAK,GAAG,OAAO,EACvC,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,KAAK,GAAG,EACrD,OAAO,CAAC,EAAE,gBAAgB,UAGlB,GAAG,SACF,KAAK,eACC,cAAc;;;;GAqE9B"}
|
package/dist/trace-wrapper.js
CHANGED
|
@@ -5,6 +5,19 @@ const api_1 = require("@opentelemetry/api");
|
|
|
5
5
|
const uuid_1 = require("uuid");
|
|
6
6
|
const debug_mode_1 = require("./debug-mode");
|
|
7
7
|
const experiment_mode_1 = require("./experiment-mode");
|
|
8
|
+
function resolveIsExperiment(globalEnabled, wrapperOptions, runOptions) {
|
|
9
|
+
if (globalEnabled)
|
|
10
|
+
return true;
|
|
11
|
+
if (typeof runOptions?.isExperiment === "boolean")
|
|
12
|
+
return runOptions.isExperiment;
|
|
13
|
+
return wrapperOptions?.isExperiment === true;
|
|
14
|
+
}
|
|
15
|
+
function normalizeThreadId(threadId) {
|
|
16
|
+
if (typeof threadId !== "string")
|
|
17
|
+
return undefined;
|
|
18
|
+
const trimmed = threadId.trim();
|
|
19
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
20
|
+
}
|
|
8
21
|
/**
|
|
9
22
|
* Wraps an agent function with OpenTelemetry tracing, automatically creating
|
|
10
23
|
* a span for the agent run and providing a `TraceContext` to the wrapped function.
|
|
@@ -13,8 +26,10 @@ const experiment_mode_1 = require("./experiment-mode");
|
|
|
13
26
|
* agent metadata (name, run ID, experiment flag), and handles error recording.
|
|
14
27
|
*
|
|
15
28
|
* `ai.agent.input` and `ai.agent.output` are set as JSON strings for Lemma
|
|
16
|
-
* ingestion and UI.
|
|
17
|
-
*
|
|
29
|
+
* ingestion and UI. When you call {@link TraceContext.onComplete}, the span
|
|
30
|
+
* ends immediately at that call — it does not wait for the wrapped function
|
|
31
|
+
* to return. If you never call `onComplete`, the span ends when the wrapped
|
|
32
|
+
* function returns or throws, and the return value is used as output.
|
|
18
33
|
*
|
|
19
34
|
* @example
|
|
20
35
|
* const myAgent = wrapAgent<{ topic: string }>(
|
|
@@ -33,15 +48,21 @@ const experiment_mode_1 = require("./experiment-mode");
|
|
|
33
48
|
* @returns An async function that accepts an `input`, executes `fn` inside a traced context, and returns `{ result, runId, span }`.
|
|
34
49
|
*/
|
|
35
50
|
function wrapAgent(agentName, fn, options) {
|
|
36
|
-
const wrappedFunction = async function (input) {
|
|
51
|
+
const wrappedFunction = async function (input, runOptions) {
|
|
37
52
|
const tracer = api_1.trace.getTracer("lemma");
|
|
38
53
|
const runId = (0, uuid_1.v4)();
|
|
54
|
+
const isExperiment = resolveIsExperiment((0, experiment_mode_1.isExperimentModeEnabled)(), options, runOptions);
|
|
55
|
+
const threadId = normalizeThreadId(runOptions?.threadId);
|
|
56
|
+
const attributes = {
|
|
57
|
+
"ai.agent.name": agentName,
|
|
58
|
+
"lemma.run_id": runId,
|
|
59
|
+
"lemma.is_experiment": isExperiment,
|
|
60
|
+
};
|
|
61
|
+
if (threadId) {
|
|
62
|
+
attributes["lemma.thread_id"] = threadId;
|
|
63
|
+
}
|
|
39
64
|
const span = tracer.startSpan("ai.agent.run", {
|
|
40
|
-
attributes
|
|
41
|
-
"ai.agent.name": agentName,
|
|
42
|
-
"lemma.run_id": runId,
|
|
43
|
-
"lemma.is_experiment": (0, experiment_mode_1.isExperimentModeEnabled)() || options?.isExperiment === true,
|
|
44
|
-
},
|
|
65
|
+
attributes,
|
|
45
66
|
}, api_1.ROOT_CONTEXT);
|
|
46
67
|
span.setAttribute("ai.agent.input", JSON.stringify(input) ?? "null");
|
|
47
68
|
(0, debug_mode_1.lemmaDebug)("trace-wrapper", "span started", { agentName, runId });
|
|
@@ -50,8 +71,11 @@ function wrapAgent(agentName, fn, options) {
|
|
|
50
71
|
try {
|
|
51
72
|
return await api_1.context.with(ctx, async () => {
|
|
52
73
|
const onComplete = (result) => {
|
|
74
|
+
if (outputSet)
|
|
75
|
+
return;
|
|
53
76
|
span.setAttribute("ai.agent.output", JSON.stringify(result) ?? "null");
|
|
54
77
|
outputSet = true;
|
|
78
|
+
span.end();
|
|
55
79
|
(0, debug_mode_1.lemmaDebug)("trace-wrapper", "onComplete called", { runId });
|
|
56
80
|
};
|
|
57
81
|
const recordError = (error) => {
|
|
@@ -61,9 +85,9 @@ function wrapAgent(agentName, fn, options) {
|
|
|
61
85
|
const result = await fn.call(this, { span, runId, onComplete, recordError }, input);
|
|
62
86
|
if (!outputSet) {
|
|
63
87
|
span.setAttribute("ai.agent.output", JSON.stringify(result) ?? "null");
|
|
88
|
+
span.end();
|
|
89
|
+
(0, debug_mode_1.lemmaDebug)("trace-wrapper", "span ended after fn returned (no onComplete)", { runId });
|
|
64
90
|
}
|
|
65
|
-
span.end();
|
|
66
|
-
(0, debug_mode_1.lemmaDebug)("trace-wrapper", "span ended after fn returned", { runId });
|
|
67
91
|
return { result, runId, span };
|
|
68
92
|
});
|
|
69
93
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-wrapper.js","sourceRoot":"","sources":["../src/trace-wrapper.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"trace-wrapper.js","sourceRoot":"","sources":["../src/trace-wrapper.ts"],"names":[],"mappings":";;AA8EA,8BA6EC;AA3JD,4CAAwE;AACxE,+BAAoC;AACpC,6CAA0C;AAC1C,uDAA4D;AA8B5D,SAAS,mBAAmB,CAC1B,aAAsB,EACtB,cAAiC,EACjC,UAA2B;IAE3B,IAAI,aAAa;QAAE,OAAO,IAAI,CAAC;IAC/B,IAAI,OAAO,UAAU,EAAE,YAAY,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC,YAAY,CAAC;IAClF,OAAO,cAAc,EAAE,YAAY,KAAK,IAAI,CAAC;AAC/C,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAiB;IAC1C,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACnD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAChC,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAClD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,SAAgB,SAAS,CACvB,SAAiB,EACjB,EAAqD,EACrD,OAA0B;IAE1B,MAAM,eAAe,GAAG,KAAK,WAE3B,KAAY,EACZ,UAA2B;QAE3B,MAAM,MAAM,GAAG,WAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,IAAA,SAAM,GAAE,CAAC;QACvB,MAAM,YAAY,GAAG,mBAAmB,CACtC,IAAA,yCAAuB,GAAE,EACzB,OAAO,EACP,UAAU,CACX,CAAC;QACF,MAAM,QAAQ,GAAG,iBAAiB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACzD,MAAM,UAAU,GAAqC;YACnD,eAAe,EAAE,SAAS;YAC1B,cAAc,EAAE,KAAK;YACrB,qBAAqB,EAAE,YAAY;SACpC,CAAC;QACF,IAAI,QAAQ,EAAE,CAAC;YACb,UAAU,CAAC,iBAAiB,CAAC,GAAG,QAAQ,CAAC;QAC3C,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAC3B,cAAc,EACd;YACE,UAAU;SACX,EACD,kBAAY,CACb,CAAC;QAEF,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC;QAErE,IAAA,uBAAU,EAAC,eAAe,EAAE,cAAc,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QAElE,MAAM,GAAG,GAAG,WAAK,CAAC,OAAO,CAAC,kBAAY,EAAE,IAAI,CAAC,CAAC;QAC9C,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,IAAI,CAAC;YACH,OAAO,MAAM,aAAO,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE;gBACxC,MAAM,UAAU,GAAG,CAAC,MAAe,EAAQ,EAAE;oBAC3C,IAAI,SAAS;wBAAE,OAAO;oBACtB,IAAI,CAAC,YAAY,CAAC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC;oBACvE,SAAS,GAAG,IAAI,CAAC;oBACjB,IAAI,CAAC,GAAG,EAAE,CAAC;oBACX,IAAA,uBAAU,EAAC,eAAe,EAAE,mBAAmB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC9D,CAAC,CAAC;gBAEF,MAAM,WAAW,GAAG,CAAC,KAAc,EAAE,EAAE;oBACrC,IAAI,CAAC,eAAe,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;oBAChF,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,uBAAuB;gBACtD,CAAC,CAAC;gBAEF,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,KAAK,CAAC,CAAC;gBAEpF,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,IAAI,CAAC,YAAY,CAAC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC;oBACvE,IAAI,CAAC,GAAG,EAAE,CAAC;oBACX,IAAA,uBAAU,EAAC,eAAe,EAAE,8CAA8C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBACzF,CAAC;gBAED,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YACjC,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,eAAe,CAAC,GAAY,CAAC,CAAC;YACnC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,uBAAuB;YACpD,IAAI,CAAC,GAAG,EAAE,CAAC;YACX,IAAA,uBAAU,EAAC,eAAe,EAAE,qBAAqB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClF,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,eAAe,CAAC;AACzB,CAAC"}
|