@assistant-ui/react 0.10.44 → 0.10.45
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/model-context/frame/AssistantFrameHost.d.ts +37 -0
- package/dist/model-context/frame/AssistantFrameHost.d.ts.map +1 -0
- package/dist/model-context/frame/AssistantFrameHost.js +151 -0
- package/dist/model-context/frame/AssistantFrameHost.js.map +1 -0
- package/dist/model-context/frame/AssistantFrameProvider.d.ts +41 -0
- package/dist/model-context/frame/AssistantFrameProvider.d.ts.map +1 -0
- package/dist/model-context/frame/AssistantFrameProvider.js +142 -0
- package/dist/model-context/frame/AssistantFrameProvider.js.map +1 -0
- package/dist/model-context/frame/AssistantFrameTypes.d.ts +29 -0
- package/dist/model-context/frame/AssistantFrameTypes.d.ts.map +1 -0
- package/dist/model-context/frame/AssistantFrameTypes.js +6 -0
- package/dist/model-context/frame/AssistantFrameTypes.js.map +1 -0
- package/dist/model-context/frame/index.d.ts +5 -0
- package/dist/model-context/frame/index.d.ts.map +1 -0
- package/dist/model-context/frame/index.js +6 -0
- package/dist/model-context/frame/index.js.map +1 -0
- package/dist/model-context/frame/useAssistantFrameHost.d.ts +28 -0
- package/dist/model-context/frame/useAssistantFrameHost.d.ts.map +1 -0
- package/dist/model-context/frame/useAssistantFrameHost.js +25 -0
- package/dist/model-context/frame/useAssistantFrameHost.js.map +1 -0
- package/dist/model-context/index.d.ts +2 -0
- package/dist/model-context/index.d.ts.map +1 -1
- package/dist/model-context/index.js +2 -0
- package/dist/model-context/index.js.map +1 -1
- package/dist/model-context/registry/ModelContextRegistry.d.ts +19 -0
- package/dist/model-context/registry/ModelContextRegistry.d.ts.map +1 -0
- package/dist/model-context/registry/ModelContextRegistry.js +117 -0
- package/dist/model-context/registry/ModelContextRegistry.js.map +1 -0
- package/dist/model-context/registry/ModelContextRegistryHandles.d.ts +14 -0
- package/dist/model-context/registry/ModelContextRegistryHandles.d.ts.map +1 -0
- package/dist/model-context/registry/ModelContextRegistryHandles.js +1 -0
- package/dist/model-context/registry/ModelContextRegistryHandles.js.map +1 -0
- package/dist/model-context/registry/index.d.ts +3 -0
- package/dist/model-context/registry/index.d.ts.map +1 -0
- package/dist/model-context/registry/index.js +4 -0
- package/dist/model-context/registry/index.js.map +1 -0
- package/dist/model-context/useAssistantInstructions.d.ts +1 -2
- package/dist/model-context/useAssistantInstructions.d.ts.map +1 -1
- package/dist/model-context/useAssistantInstructions.js.map +1 -1
- package/package.json +3 -3
- package/src/model-context/frame/AssistantFrame.test.ts +353 -0
- package/src/model-context/frame/AssistantFrameHost.ts +218 -0
- package/src/model-context/frame/AssistantFrameProvider.ts +225 -0
- package/src/model-context/frame/AssistantFrameTypes.ts +40 -0
- package/src/model-context/frame/SPEC_AssistantFrame.md +104 -0
- package/src/model-context/frame/index.ts +4 -0
- package/src/model-context/frame/useAssistantFrameHost.ts +48 -0
- package/src/model-context/index.ts +3 -0
- package/src/model-context/registry/ModelContextRegistry.ts +165 -0
- package/src/model-context/registry/ModelContextRegistryHandles.ts +19 -0
- package/src/model-context/registry/SPEC_ModelContextRegistry.md +40 -0
- package/src/model-context/registry/index.ts +2 -0
- package/src/model-context/useAssistantInstructions.tsx +1 -1
@@ -0,0 +1,117 @@
|
|
1
|
+
// src/model-context/registry/ModelContextRegistry.ts
|
2
|
+
import {
|
3
|
+
mergeModelContexts
|
4
|
+
} from "../../model-context/ModelContextTypes.js";
|
5
|
+
var ModelContextRegistry = class {
|
6
|
+
_tools = /* @__PURE__ */ new Map();
|
7
|
+
_instructions = /* @__PURE__ */ new Map();
|
8
|
+
_providers = /* @__PURE__ */ new Map();
|
9
|
+
_subscribers = /* @__PURE__ */ new Set();
|
10
|
+
_providerUnsubscribes = /* @__PURE__ */ new Map();
|
11
|
+
getModelContext() {
|
12
|
+
const instructions = Array.from(this._instructions.values()).filter(
|
13
|
+
Boolean
|
14
|
+
);
|
15
|
+
const system = instructions.length > 0 ? instructions.join("\n\n") : void 0;
|
16
|
+
const tools = {};
|
17
|
+
for (const toolProps of this._tools.values()) {
|
18
|
+
const { toolName, render, ...tool } = toolProps;
|
19
|
+
tools[toolName] = tool;
|
20
|
+
}
|
21
|
+
const providerContexts = mergeModelContexts(
|
22
|
+
new Set(this._providers.values())
|
23
|
+
);
|
24
|
+
const context = {
|
25
|
+
system,
|
26
|
+
tools: Object.keys(tools).length > 0 ? tools : void 0
|
27
|
+
};
|
28
|
+
if (providerContexts.system) {
|
29
|
+
context.system = context.system ? `${context.system}
|
30
|
+
|
31
|
+
${providerContexts.system}` : providerContexts.system;
|
32
|
+
}
|
33
|
+
if (providerContexts.tools) {
|
34
|
+
context.tools = { ...context.tools || {}, ...providerContexts.tools };
|
35
|
+
}
|
36
|
+
if (providerContexts.callSettings) {
|
37
|
+
context.callSettings = providerContexts.callSettings;
|
38
|
+
}
|
39
|
+
if (providerContexts.config) {
|
40
|
+
context.config = providerContexts.config;
|
41
|
+
}
|
42
|
+
return context;
|
43
|
+
}
|
44
|
+
subscribe(callback) {
|
45
|
+
this._subscribers.add(callback);
|
46
|
+
return () => this._subscribers.delete(callback);
|
47
|
+
}
|
48
|
+
notifySubscribers() {
|
49
|
+
for (const callback of this._subscribers) {
|
50
|
+
callback();
|
51
|
+
}
|
52
|
+
}
|
53
|
+
addTool(tool) {
|
54
|
+
const id = Symbol();
|
55
|
+
this._tools.set(id, tool);
|
56
|
+
this.notifySubscribers();
|
57
|
+
return {
|
58
|
+
update: (newTool) => {
|
59
|
+
if (this._tools.has(id)) {
|
60
|
+
this._tools.set(id, newTool);
|
61
|
+
this.notifySubscribers();
|
62
|
+
}
|
63
|
+
},
|
64
|
+
remove: () => {
|
65
|
+
this._tools.delete(id);
|
66
|
+
this.notifySubscribers();
|
67
|
+
}
|
68
|
+
};
|
69
|
+
}
|
70
|
+
addInstruction(config) {
|
71
|
+
const id = Symbol();
|
72
|
+
const instruction = typeof config === "string" ? config : config.instruction;
|
73
|
+
const disabled = typeof config === "object" ? config.disabled : false;
|
74
|
+
if (!disabled) {
|
75
|
+
this._instructions.set(id, instruction);
|
76
|
+
this.notifySubscribers();
|
77
|
+
}
|
78
|
+
return {
|
79
|
+
update: (newConfig) => {
|
80
|
+
const newInstruction = typeof newConfig === "string" ? newConfig : newConfig.instruction;
|
81
|
+
const newDisabled = typeof newConfig === "object" ? newConfig.disabled : false;
|
82
|
+
if (newDisabled) {
|
83
|
+
this._instructions.delete(id);
|
84
|
+
} else {
|
85
|
+
this._instructions.set(id, newInstruction);
|
86
|
+
}
|
87
|
+
this.notifySubscribers();
|
88
|
+
},
|
89
|
+
remove: () => {
|
90
|
+
this._instructions.delete(id);
|
91
|
+
this.notifySubscribers();
|
92
|
+
}
|
93
|
+
};
|
94
|
+
}
|
95
|
+
addProvider(provider) {
|
96
|
+
const id = Symbol();
|
97
|
+
this._providers.set(id, provider);
|
98
|
+
const unsubscribe = provider.subscribe?.(() => {
|
99
|
+
this.notifySubscribers();
|
100
|
+
});
|
101
|
+
this._providerUnsubscribes.set(id, unsubscribe);
|
102
|
+
this.notifySubscribers();
|
103
|
+
return {
|
104
|
+
remove: () => {
|
105
|
+
this._providers.delete(id);
|
106
|
+
const unsubscribe2 = this._providerUnsubscribes.get(id);
|
107
|
+
unsubscribe2?.();
|
108
|
+
this._providerUnsubscribes.delete(id);
|
109
|
+
this.notifySubscribers();
|
110
|
+
}
|
111
|
+
};
|
112
|
+
}
|
113
|
+
};
|
114
|
+
export {
|
115
|
+
ModelContextRegistry
|
116
|
+
};
|
117
|
+
//# sourceMappingURL=ModelContextRegistry.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../../src/model-context/registry/ModelContextRegistry.ts"],"sourcesContent":["import { Tool } from \"assistant-stream\";\nimport {\n ModelContext,\n ModelContextProvider,\n mergeModelContexts,\n} from \"../../model-context/ModelContextTypes\";\nimport { Unsubscribe } from \"../../types/Unsubscribe\";\nimport {\n ModelContextRegistryToolHandle,\n ModelContextRegistryInstructionHandle,\n ModelContextRegistryProviderHandle,\n} from \"./ModelContextRegistryHandles\";\nimport type { AssistantToolProps } from \"../../model-context/useAssistantTool\";\nimport type { AssistantInstructionsConfig } from \"../../model-context/useAssistantInstructions\";\n\nexport class ModelContextRegistry implements ModelContextProvider {\n private _tools = new Map<symbol, AssistantToolProps<any, any>>();\n private _instructions = new Map<symbol, string>();\n private _providers = new Map<symbol, ModelContextProvider>();\n private _subscribers = new Set<() => void>();\n private _providerUnsubscribes = new Map<symbol, Unsubscribe | undefined>();\n\n getModelContext(): ModelContext {\n // Merge instructions\n const instructions = Array.from(this._instructions.values()).filter(\n Boolean,\n );\n\n const system =\n instructions.length > 0 ? instructions.join(\"\\n\\n\") : undefined;\n\n // Collect tools\n const tools: Record<string, Tool<any, any>> = {};\n for (const toolProps of this._tools.values()) {\n const { toolName, render, ...tool } = toolProps;\n tools[toolName] = tool;\n }\n\n // Merge provider contexts\n const providerContexts = mergeModelContexts(\n new Set(this._providers.values()),\n );\n\n // Combine everything\n const context: ModelContext = {\n system,\n tools: Object.keys(tools).length > 0 ? tools : undefined,\n };\n\n // Merge with provider contexts\n if (providerContexts.system) {\n context.system = context.system\n ? `${context.system}\\n\\n${providerContexts.system}`\n : providerContexts.system;\n }\n\n if (providerContexts.tools) {\n context.tools = { ...(context.tools || {}), ...providerContexts.tools };\n }\n\n if (providerContexts.callSettings) {\n context.callSettings = providerContexts.callSettings;\n }\n\n if (providerContexts.config) {\n context.config = providerContexts.config;\n }\n\n return context;\n }\n\n subscribe(callback: () => void): Unsubscribe {\n this._subscribers.add(callback);\n return () => this._subscribers.delete(callback);\n }\n\n private notifySubscribers(): void {\n for (const callback of this._subscribers) {\n callback();\n }\n }\n\n addTool<TArgs extends Record<string, unknown>, TResult>(\n tool: AssistantToolProps<TArgs, TResult>,\n ): ModelContextRegistryToolHandle<TArgs, TResult> {\n const id = Symbol();\n\n this._tools.set(id, tool);\n this.notifySubscribers();\n\n return {\n update: (newTool: AssistantToolProps<TArgs, TResult>) => {\n if (this._tools.has(id)) {\n this._tools.set(id, newTool);\n this.notifySubscribers();\n }\n },\n remove: () => {\n this._tools.delete(id);\n this.notifySubscribers();\n },\n };\n }\n\n addInstruction(\n config: string | AssistantInstructionsConfig,\n ): ModelContextRegistryInstructionHandle {\n const id = Symbol();\n\n const instruction =\n typeof config === \"string\" ? config : config.instruction;\n const disabled = typeof config === \"object\" ? config.disabled : false;\n\n if (!disabled) {\n this._instructions.set(id, instruction);\n this.notifySubscribers();\n }\n\n return {\n update: (newConfig: string | AssistantInstructionsConfig) => {\n const newInstruction =\n typeof newConfig === \"string\" ? newConfig : newConfig.instruction;\n const newDisabled =\n typeof newConfig === \"object\" ? newConfig.disabled : false;\n\n if (newDisabled) {\n this._instructions.delete(id);\n } else {\n this._instructions.set(id, newInstruction);\n }\n this.notifySubscribers();\n },\n remove: () => {\n this._instructions.delete(id);\n this.notifySubscribers();\n },\n };\n }\n\n addProvider(\n provider: ModelContextProvider,\n ): ModelContextRegistryProviderHandle {\n const id = Symbol();\n\n this._providers.set(id, provider);\n\n // Subscribe to provider changes\n const unsubscribe = provider.subscribe?.(() => {\n this.notifySubscribers();\n });\n this._providerUnsubscribes.set(id, unsubscribe);\n\n this.notifySubscribers();\n\n return {\n remove: () => {\n this._providers.delete(id);\n const unsubscribe = this._providerUnsubscribes.get(id);\n unsubscribe?.();\n this._providerUnsubscribes.delete(id);\n this.notifySubscribers();\n },\n };\n }\n}\n"],"mappings":";AACA;AAAA,EAGE;AAAA,OACK;AAUA,IAAM,uBAAN,MAA2D;AAAA,EACxD,SAAS,oBAAI,IAA0C;AAAA,EACvD,gBAAgB,oBAAI,IAAoB;AAAA,EACxC,aAAa,oBAAI,IAAkC;AAAA,EACnD,eAAe,oBAAI,IAAgB;AAAA,EACnC,wBAAwB,oBAAI,IAAqC;AAAA,EAEzE,kBAAgC;AAE9B,UAAM,eAAe,MAAM,KAAK,KAAK,cAAc,OAAO,CAAC,EAAE;AAAA,MAC3D;AAAA,IACF;AAEA,UAAM,SACJ,aAAa,SAAS,IAAI,aAAa,KAAK,MAAM,IAAI;AAGxD,UAAM,QAAwC,CAAC;AAC/C,eAAW,aAAa,KAAK,OAAO,OAAO,GAAG;AAC5C,YAAM,EAAE,UAAU,QAAQ,GAAG,KAAK,IAAI;AACtC,YAAM,QAAQ,IAAI;AAAA,IACpB;AAGA,UAAM,mBAAmB;AAAA,MACvB,IAAI,IAAI,KAAK,WAAW,OAAO,CAAC;AAAA,IAClC;AAGA,UAAM,UAAwB;AAAA,MAC5B;AAAA,MACA,OAAO,OAAO,KAAK,KAAK,EAAE,SAAS,IAAI,QAAQ;AAAA,IACjD;AAGA,QAAI,iBAAiB,QAAQ;AAC3B,cAAQ,SAAS,QAAQ,SACrB,GAAG,QAAQ,MAAM;AAAA;AAAA,EAAO,iBAAiB,MAAM,KAC/C,iBAAiB;AAAA,IACvB;AAEA,QAAI,iBAAiB,OAAO;AAC1B,cAAQ,QAAQ,EAAE,GAAI,QAAQ,SAAS,CAAC,GAAI,GAAG,iBAAiB,MAAM;AAAA,IACxE;AAEA,QAAI,iBAAiB,cAAc;AACjC,cAAQ,eAAe,iBAAiB;AAAA,IAC1C;AAEA,QAAI,iBAAiB,QAAQ;AAC3B,cAAQ,SAAS,iBAAiB;AAAA,IACpC;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,UAAmC;AAC3C,SAAK,aAAa,IAAI,QAAQ;AAC9B,WAAO,MAAM,KAAK,aAAa,OAAO,QAAQ;AAAA,EAChD;AAAA,EAEQ,oBAA0B;AAChC,eAAW,YAAY,KAAK,cAAc;AACxC,eAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,QACE,MACgD;AAChD,UAAM,KAAK,OAAO;AAElB,SAAK,OAAO,IAAI,IAAI,IAAI;AACxB,SAAK,kBAAkB;AAEvB,WAAO;AAAA,MACL,QAAQ,CAAC,YAAgD;AACvD,YAAI,KAAK,OAAO,IAAI,EAAE,GAAG;AACvB,eAAK,OAAO,IAAI,IAAI,OAAO;AAC3B,eAAK,kBAAkB;AAAA,QACzB;AAAA,MACF;AAAA,MACA,QAAQ,MAAM;AACZ,aAAK,OAAO,OAAO,EAAE;AACrB,aAAK,kBAAkB;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,eACE,QACuC;AACvC,UAAM,KAAK,OAAO;AAElB,UAAM,cACJ,OAAO,WAAW,WAAW,SAAS,OAAO;AAC/C,UAAM,WAAW,OAAO,WAAW,WAAW,OAAO,WAAW;AAEhE,QAAI,CAAC,UAAU;AACb,WAAK,cAAc,IAAI,IAAI,WAAW;AACtC,WAAK,kBAAkB;AAAA,IACzB;AAEA,WAAO;AAAA,MACL,QAAQ,CAAC,cAAoD;AAC3D,cAAM,iBACJ,OAAO,cAAc,WAAW,YAAY,UAAU;AACxD,cAAM,cACJ,OAAO,cAAc,WAAW,UAAU,WAAW;AAEvD,YAAI,aAAa;AACf,eAAK,cAAc,OAAO,EAAE;AAAA,QAC9B,OAAO;AACL,eAAK,cAAc,IAAI,IAAI,cAAc;AAAA,QAC3C;AACA,aAAK,kBAAkB;AAAA,MACzB;AAAA,MACA,QAAQ,MAAM;AACZ,aAAK,cAAc,OAAO,EAAE;AAC5B,aAAK,kBAAkB;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YACE,UACoC;AACpC,UAAM,KAAK,OAAO;AAElB,SAAK,WAAW,IAAI,IAAI,QAAQ;AAGhC,UAAM,cAAc,SAAS,YAAY,MAAM;AAC7C,WAAK,kBAAkB;AAAA,IACzB,CAAC;AACD,SAAK,sBAAsB,IAAI,IAAI,WAAW;AAE9C,SAAK,kBAAkB;AAEvB,WAAO;AAAA,MACL,QAAQ,MAAM;AACZ,aAAK,WAAW,OAAO,EAAE;AACzB,cAAMA,eAAc,KAAK,sBAAsB,IAAI,EAAE;AACrD,QAAAA,eAAc;AACd,aAAK,sBAAsB,OAAO,EAAE;AACpC,aAAK,kBAAkB;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AACF;","names":["unsubscribe"]}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import type { AssistantToolProps } from "../../model-context/useAssistantTool";
|
2
|
+
import type { AssistantInstructionsConfig } from "../../model-context/useAssistantInstructions";
|
3
|
+
export interface ModelContextRegistryToolHandle<TArgs extends Record<string, unknown> = any, TResult = any> {
|
4
|
+
update(tool: AssistantToolProps<TArgs, TResult>): void;
|
5
|
+
remove(): void;
|
6
|
+
}
|
7
|
+
export interface ModelContextRegistryInstructionHandle {
|
8
|
+
update(config: string | AssistantInstructionsConfig): void;
|
9
|
+
remove(): void;
|
10
|
+
}
|
11
|
+
export interface ModelContextRegistryProviderHandle {
|
12
|
+
remove(): void;
|
13
|
+
}
|
14
|
+
//# sourceMappingURL=ModelContextRegistryHandles.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"ModelContextRegistryHandles.d.ts","sourceRoot":"","sources":["../../../src/model-context/registry/ModelContextRegistryHandles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,8CAA8C,CAAC;AAEhG,MAAM,WAAW,8BAA8B,CAC7C,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,GAAG,EAC3C,OAAO,GAAG,GAAG;IAEb,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACvD,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,qCAAqC;IACpD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,2BAA2B,GAAG,IAAI,CAAC;IAC3D,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,kCAAkC;IACjD,MAAM,IAAI,IAAI,CAAC;CAChB"}
|
@@ -0,0 +1 @@
|
|
1
|
+
//# sourceMappingURL=ModelContextRegistryHandles.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/model-context/registry/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../../src/model-context/registry/index.ts"],"sourcesContent":["export * from \"./ModelContextRegistry\";\nexport * from \"./ModelContextRegistryHandles\";\n"],"mappings":";AAAA,cAAc;AACd,cAAc;","names":[]}
|
@@ -1,7 +1,6 @@
|
|
1
|
-
type AssistantInstructionsConfig = {
|
1
|
+
export type AssistantInstructionsConfig = {
|
2
2
|
disabled?: boolean | undefined;
|
3
3
|
instruction: string;
|
4
4
|
};
|
5
5
|
export declare const useAssistantInstructions: (config: string | AssistantInstructionsConfig) => void;
|
6
|
-
export {};
|
7
6
|
//# sourceMappingURL=useAssistantInstructions.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"useAssistantInstructions.d.ts","sourceRoot":"","sources":["../../src/model-context/useAssistantInstructions.tsx"],"names":[],"mappings":"AAKA,
|
1
|
+
{"version":3,"file":"useAssistantInstructions.d.ts","sourceRoot":"","sources":["../../src/model-context/useAssistantInstructions.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AASF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,MAAM,GAAG,2BAA2B,SAe7C,CAAC"}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/model-context/useAssistantInstructions.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect } from \"react\";\nimport { useAssistantRuntime } from \"../context\";\n\
|
1
|
+
{"version":3,"sources":["../../src/model-context/useAssistantInstructions.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect } from \"react\";\nimport { useAssistantRuntime } from \"../context\";\n\nexport type AssistantInstructionsConfig = {\n disabled?: boolean | undefined;\n instruction: string;\n};\n\nconst getInstructions = (\n instruction: string | AssistantInstructionsConfig,\n): AssistantInstructionsConfig => {\n if (typeof instruction === \"string\") return { instruction };\n return instruction;\n};\n\nexport const useAssistantInstructions = (\n config: string | AssistantInstructionsConfig,\n) => {\n const { instruction, disabled = false } = getInstructions(config);\n const assistantRuntime = useAssistantRuntime();\n\n useEffect(() => {\n if (disabled) return;\n\n const config = {\n system: instruction,\n };\n return assistantRuntime.registerModelContextProvider({\n getModelContext: () => config,\n });\n }, [assistantRuntime, instruction, disabled]);\n};\n"],"mappings":";;;AAEA,SAAS,iBAAiB;AAC1B,SAAS,2BAA2B;AAOpC,IAAM,kBAAkB,CACtB,gBACgC;AAChC,MAAI,OAAO,gBAAgB,SAAU,QAAO,EAAE,YAAY;AAC1D,SAAO;AACT;AAEO,IAAM,2BAA2B,CACtC,WACG;AACH,QAAM,EAAE,aAAa,WAAW,MAAM,IAAI,gBAAgB,MAAM;AAChE,QAAM,mBAAmB,oBAAoB;AAE7C,YAAU,MAAM;AACd,QAAI,SAAU;AAEd,UAAMA,UAAS;AAAA,MACb,QAAQ;AAAA,IACV;AACA,WAAO,iBAAiB,6BAA6B;AAAA,MACnD,iBAAiB,MAAMA;AAAA,IACzB,CAAC;AAAA,EACH,GAAG,CAAC,kBAAkB,aAAa,QAAQ,CAAC;AAC9C;","names":["config"]}
|
package/package.json
CHANGED
@@ -28,7 +28,7 @@
|
|
28
28
|
"conversational-ui",
|
29
29
|
"conversational-ai"
|
30
30
|
],
|
31
|
-
"version": "0.10.
|
31
|
+
"version": "0.10.45",
|
32
32
|
"license": "MIT",
|
33
33
|
"type": "module",
|
34
34
|
"exports": {
|
@@ -48,6 +48,7 @@
|
|
48
48
|
],
|
49
49
|
"sideEffects": false,
|
50
50
|
"dependencies": {
|
51
|
+
"assistant-cloud": "^0.1.1",
|
51
52
|
"@radix-ui/primitive": "^1.1.3",
|
52
53
|
"@radix-ui/react-compose-refs": "^1.1.2",
|
53
54
|
"@radix-ui/react-context": "^1.1.2",
|
@@ -62,8 +63,7 @@
|
|
62
63
|
"nanoid": "5.1.5",
|
63
64
|
"react-textarea-autosize": "^8.5.9",
|
64
65
|
"zod": "^4.0.17",
|
65
|
-
"zustand": "^5.0.7"
|
66
|
-
"assistant-cloud": "0.1.1"
|
66
|
+
"zustand": "^5.0.7"
|
67
67
|
},
|
68
68
|
"peerDependencies": {
|
69
69
|
"@types/react": "*",
|
@@ -0,0 +1,353 @@
|
|
1
|
+
/**
|
2
|
+
* @vitest-environment jsdom
|
3
|
+
*/
|
4
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
5
|
+
import { AssistantFrameProvider } from "./AssistantFrameProvider";
|
6
|
+
import { AssistantFrameHost } from "./AssistantFrameHost";
|
7
|
+
import { ModelContextRegistry } from "../registry/ModelContextRegistry";
|
8
|
+
import z from "zod";
|
9
|
+
|
10
|
+
describe("AssistantFrame Integration", () => {
|
11
|
+
let messageHandlers: Map<string, (event: MessageEvent) => void>;
|
12
|
+
let iframeWindow: Window;
|
13
|
+
let parentWindow: any;
|
14
|
+
|
15
|
+
beforeEach(() => {
|
16
|
+
messageHandlers = new Map();
|
17
|
+
|
18
|
+
// Create a mock parent window that the iframe can post back to
|
19
|
+
parentWindow = {
|
20
|
+
postMessage: vi.fn((data: any) => {
|
21
|
+
// When iframe posts to parent, deliver to parent handler
|
22
|
+
const parentHandler = messageHandlers.get("parent");
|
23
|
+
if (parentHandler) {
|
24
|
+
Promise.resolve().then(() => {
|
25
|
+
parentHandler({
|
26
|
+
data,
|
27
|
+
source: iframeWindow,
|
28
|
+
origin: "*",
|
29
|
+
} as MessageEvent);
|
30
|
+
});
|
31
|
+
}
|
32
|
+
}),
|
33
|
+
};
|
34
|
+
|
35
|
+
// Create mock iframe window with proper message routing
|
36
|
+
iframeWindow = {
|
37
|
+
postMessage: vi.fn((data: any) => {
|
38
|
+
// Route message to iframe handler (provider)
|
39
|
+
const iframeHandler = messageHandlers.get("iframe");
|
40
|
+
if (iframeHandler) {
|
41
|
+
Promise.resolve().then(() => {
|
42
|
+
iframeHandler({
|
43
|
+
data,
|
44
|
+
source: parentWindow, // parent window is the source for subscription
|
45
|
+
origin: "*",
|
46
|
+
} as MessageEvent);
|
47
|
+
});
|
48
|
+
}
|
49
|
+
}),
|
50
|
+
} as any;
|
51
|
+
|
52
|
+
// Mock window.parent for iframe to broadcast to
|
53
|
+
Object.defineProperty(window, "parent", {
|
54
|
+
value: parentWindow,
|
55
|
+
writable: true,
|
56
|
+
configurable: true,
|
57
|
+
});
|
58
|
+
|
59
|
+
// Mock window methods for message passing
|
60
|
+
vi.spyOn(window, "addEventListener").mockImplementation(
|
61
|
+
(event: string, handler: any) => {
|
62
|
+
if (event === "message") {
|
63
|
+
// Store both handlers - we'll determine which is which based on usage
|
64
|
+
if (!messageHandlers.has("iframe")) {
|
65
|
+
messageHandlers.set("iframe", handler); // First registration is provider
|
66
|
+
} else {
|
67
|
+
messageHandlers.set("parent", handler); // Second is host
|
68
|
+
}
|
69
|
+
}
|
70
|
+
},
|
71
|
+
);
|
72
|
+
|
73
|
+
vi.spyOn(window, "removeEventListener").mockImplementation(() => {});
|
74
|
+
|
75
|
+
vi.spyOn(window, "postMessage").mockImplementation(() => {
|
76
|
+
// This shouldn't be called in our test setup
|
77
|
+
});
|
78
|
+
});
|
79
|
+
|
80
|
+
afterEach(() => {
|
81
|
+
// Clean up
|
82
|
+
vi.restoreAllMocks();
|
83
|
+
AssistantFrameProvider.dispose();
|
84
|
+
messageHandlers.clear();
|
85
|
+
});
|
86
|
+
|
87
|
+
it("should establish connection between host and provider", async () => {
|
88
|
+
// Setup provider in iframe
|
89
|
+
const registry = new ModelContextRegistry();
|
90
|
+
const unsubscribe =
|
91
|
+
AssistantFrameProvider.addModelContextProvider(registry);
|
92
|
+
|
93
|
+
// Setup host in parent
|
94
|
+
const host = new AssistantFrameHost(iframeWindow);
|
95
|
+
|
96
|
+
// Wait for connection
|
97
|
+
await vi.waitFor(() => {
|
98
|
+
expect(iframeWindow.postMessage).toHaveBeenCalledWith(
|
99
|
+
expect.objectContaining({
|
100
|
+
channel: "assistant-ui-frame",
|
101
|
+
message: expect.objectContaining({
|
102
|
+
type: "model-context-request",
|
103
|
+
}),
|
104
|
+
}),
|
105
|
+
"*",
|
106
|
+
);
|
107
|
+
});
|
108
|
+
|
109
|
+
// Clean up
|
110
|
+
host.dispose();
|
111
|
+
unsubscribe();
|
112
|
+
});
|
113
|
+
|
114
|
+
it("should sync tools from provider to host", async () => {
|
115
|
+
// Setup provider with tools
|
116
|
+
const registry = new ModelContextRegistry();
|
117
|
+
|
118
|
+
const toolExecute = vi.fn().mockResolvedValue({ result: "search results" });
|
119
|
+
registry.addTool({
|
120
|
+
toolName: "search",
|
121
|
+
description: "Search the web",
|
122
|
+
parameters: z.object({ query: z.string() }),
|
123
|
+
execute: toolExecute,
|
124
|
+
});
|
125
|
+
|
126
|
+
const unsubscribe =
|
127
|
+
AssistantFrameProvider.addModelContextProvider(registry);
|
128
|
+
|
129
|
+
// Setup host
|
130
|
+
const host = new AssistantFrameHost(iframeWindow);
|
131
|
+
|
132
|
+
// Wait for connection and initial sync
|
133
|
+
await vi.waitFor(() => {
|
134
|
+
const context = host.getModelContext();
|
135
|
+
expect(context.tools).toBeDefined();
|
136
|
+
expect(context.tools?.search).toBeDefined();
|
137
|
+
expect(context.tools?.search.description).toBe("Search the web");
|
138
|
+
});
|
139
|
+
|
140
|
+
// Clean up
|
141
|
+
host.dispose();
|
142
|
+
unsubscribe();
|
143
|
+
});
|
144
|
+
|
145
|
+
it("should execute tools through the frame boundary", async () => {
|
146
|
+
// Setup provider with executable tool
|
147
|
+
const registry = new ModelContextRegistry();
|
148
|
+
|
149
|
+
const toolExecute = vi
|
150
|
+
.fn()
|
151
|
+
.mockResolvedValue({ results: ["result1", "result2"] });
|
152
|
+
registry.addTool({
|
153
|
+
toolName: "search",
|
154
|
+
description: "Search the web",
|
155
|
+
parameters: z.object({ query: z.string() }),
|
156
|
+
execute: toolExecute,
|
157
|
+
});
|
158
|
+
|
159
|
+
const unsubscribe =
|
160
|
+
AssistantFrameProvider.addModelContextProvider(registry);
|
161
|
+
|
162
|
+
// Setup host
|
163
|
+
const host = new AssistantFrameHost(iframeWindow);
|
164
|
+
|
165
|
+
// Wait for tools to be available
|
166
|
+
await vi.waitFor(() => {
|
167
|
+
const context = host.getModelContext();
|
168
|
+
expect(context.tools?.search).toBeDefined();
|
169
|
+
});
|
170
|
+
|
171
|
+
// Execute tool through host
|
172
|
+
const context = host.getModelContext();
|
173
|
+
const searchTool = context.tools?.search;
|
174
|
+
|
175
|
+
const resultPromise = searchTool!.execute!(
|
176
|
+
{ query: "test query" },
|
177
|
+
{} as any,
|
178
|
+
);
|
179
|
+
|
180
|
+
// Wait for tool execution
|
181
|
+
await vi.waitFor(() => {
|
182
|
+
expect(toolExecute).toHaveBeenCalledWith(
|
183
|
+
{ query: "test query" },
|
184
|
+
expect.objectContaining({
|
185
|
+
toolCallId: expect.any(String),
|
186
|
+
abortSignal: expect.any(AbortSignal),
|
187
|
+
}),
|
188
|
+
);
|
189
|
+
});
|
190
|
+
|
191
|
+
const result = await resultPromise;
|
192
|
+
expect(result).toEqual({ results: ["result1", "result2"] });
|
193
|
+
|
194
|
+
// Clean up
|
195
|
+
host.dispose();
|
196
|
+
unsubscribe();
|
197
|
+
});
|
198
|
+
|
199
|
+
it("should handle tool execution errors", async () => {
|
200
|
+
// Setup provider with failing tool
|
201
|
+
const registry = new ModelContextRegistry();
|
202
|
+
|
203
|
+
const toolExecute = vi
|
204
|
+
.fn()
|
205
|
+
.mockRejectedValue(new Error("Tool execution failed"));
|
206
|
+
registry.addTool({
|
207
|
+
toolName: "failingTool",
|
208
|
+
description: "A tool that fails",
|
209
|
+
parameters: z.object({ input: z.string() }),
|
210
|
+
execute: toolExecute,
|
211
|
+
});
|
212
|
+
|
213
|
+
const unsubscribe =
|
214
|
+
AssistantFrameProvider.addModelContextProvider(registry);
|
215
|
+
|
216
|
+
// Setup host
|
217
|
+
const host = new AssistantFrameHost(iframeWindow);
|
218
|
+
|
219
|
+
// Wait for tools to be available
|
220
|
+
await vi.waitFor(() => {
|
221
|
+
const context = host.getModelContext();
|
222
|
+
expect(context.tools?.failingTool).toBeDefined();
|
223
|
+
});
|
224
|
+
|
225
|
+
// Execute tool and expect error
|
226
|
+
const context = host.getModelContext();
|
227
|
+
const failingTool = context.tools?.failingTool;
|
228
|
+
|
229
|
+
await expect(
|
230
|
+
failingTool!.execute!({ input: "test" }, {} as any),
|
231
|
+
).rejects.toThrow("Tool execution failed");
|
232
|
+
|
233
|
+
// Clean up
|
234
|
+
host.dispose();
|
235
|
+
unsubscribe();
|
236
|
+
});
|
237
|
+
|
238
|
+
it("should handle multiple providers", async () => {
|
239
|
+
// Setup multiple providers
|
240
|
+
const registry1 = new ModelContextRegistry();
|
241
|
+
const registry2 = new ModelContextRegistry();
|
242
|
+
|
243
|
+
registry1.addTool({
|
244
|
+
toolName: "tool1",
|
245
|
+
description: "First tool",
|
246
|
+
parameters: z.object({ input: z.string() }),
|
247
|
+
execute: async () => ({ from: "tool1" }),
|
248
|
+
});
|
249
|
+
|
250
|
+
registry2.addTool({
|
251
|
+
toolName: "tool2",
|
252
|
+
description: "Second tool",
|
253
|
+
parameters: z.object({ input: z.string() }),
|
254
|
+
execute: async () => ({ from: "tool2" }),
|
255
|
+
});
|
256
|
+
|
257
|
+
const unsub1 = AssistantFrameProvider.addModelContextProvider(registry1);
|
258
|
+
const unsub2 = AssistantFrameProvider.addModelContextProvider(registry2);
|
259
|
+
|
260
|
+
// Setup host
|
261
|
+
const host = new AssistantFrameHost(iframeWindow);
|
262
|
+
|
263
|
+
// Wait for both tools to be available
|
264
|
+
await vi.waitFor(() => {
|
265
|
+
const context = host.getModelContext();
|
266
|
+
expect(context.tools?.tool1).toBeDefined();
|
267
|
+
expect(context.tools?.tool2).toBeDefined();
|
268
|
+
});
|
269
|
+
|
270
|
+
// Clean up
|
271
|
+
host.dispose();
|
272
|
+
unsub1();
|
273
|
+
unsub2();
|
274
|
+
});
|
275
|
+
|
276
|
+
it("should merge system instructions from multiple providers", async () => {
|
277
|
+
// Setup providers with system instructions
|
278
|
+
const registry1 = new ModelContextRegistry();
|
279
|
+
const registry2 = new ModelContextRegistry();
|
280
|
+
|
281
|
+
registry1.addInstruction("You are a helpful assistant.");
|
282
|
+
registry2.addInstruction("Always be concise.");
|
283
|
+
|
284
|
+
const unsub1 = AssistantFrameProvider.addModelContextProvider(registry1);
|
285
|
+
const unsub2 = AssistantFrameProvider.addModelContextProvider(registry2);
|
286
|
+
|
287
|
+
// Setup host
|
288
|
+
const host = new AssistantFrameHost(iframeWindow);
|
289
|
+
|
290
|
+
// Wait for instructions to be synced
|
291
|
+
await vi.waitFor(() => {
|
292
|
+
const context = host.getModelContext();
|
293
|
+
expect(context.system).toBeDefined();
|
294
|
+
expect(context.system).toContain("You are a helpful assistant.");
|
295
|
+
expect(context.system).toContain("Always be concise.");
|
296
|
+
});
|
297
|
+
|
298
|
+
// Clean up
|
299
|
+
host.dispose();
|
300
|
+
unsub1();
|
301
|
+
unsub2();
|
302
|
+
});
|
303
|
+
|
304
|
+
it("should act as empty ModelContextProvider when iframe has no providers", async () => {
|
305
|
+
// Don't register any providers in the iframe
|
306
|
+
// This simulates an iframe that doesn't respond to model-context requests
|
307
|
+
|
308
|
+
// Setup host
|
309
|
+
const host = new AssistantFrameHost(iframeWindow);
|
310
|
+
|
311
|
+
// Host should immediately return empty context
|
312
|
+
const context = host.getModelContext();
|
313
|
+
expect(context).toEqual({});
|
314
|
+
|
315
|
+
// Wait a bit to ensure no errors occur
|
316
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
317
|
+
|
318
|
+
// Context should still be empty
|
319
|
+
expect(host.getModelContext()).toEqual({});
|
320
|
+
|
321
|
+
// Clean up
|
322
|
+
host.dispose();
|
323
|
+
});
|
324
|
+
|
325
|
+
it("should clean up properly on dispose", async () => {
|
326
|
+
// Setup provider
|
327
|
+
const registry = new ModelContextRegistry();
|
328
|
+
|
329
|
+
const unsubscribe =
|
330
|
+
AssistantFrameProvider.addModelContextProvider(registry);
|
331
|
+
|
332
|
+
// Setup host
|
333
|
+
const host = new AssistantFrameHost(iframeWindow);
|
334
|
+
|
335
|
+
// Wait for connection
|
336
|
+
await vi.waitFor(() => {
|
337
|
+
expect(iframeWindow.postMessage).toHaveBeenCalled();
|
338
|
+
});
|
339
|
+
|
340
|
+
// Dispose host
|
341
|
+
host.dispose();
|
342
|
+
|
343
|
+
// Verify event listener was removed (no unsubscribe message in new design)
|
344
|
+
expect(window.removeEventListener).toHaveBeenCalledWith(
|
345
|
+
"message",
|
346
|
+
expect.any(Function),
|
347
|
+
);
|
348
|
+
|
349
|
+
// Clean up provider
|
350
|
+
unsubscribe();
|
351
|
+
AssistantFrameProvider.dispose();
|
352
|
+
});
|
353
|
+
});
|