@apollo/client-ai-apps 0.3.0 → 0.3.2
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/.github/workflows/compare-build-output.yml +28 -0
- package/config/compare-build-output-to.sh +90 -0
- package/dist/core/ApolloClient.d.ts +14 -0
- package/dist/index.d.ts +17 -11
- package/dist/index.js +96 -44
- package/dist/react/ApolloProvider.d.ts +9 -0
- package/dist/react/context/ToolUseContext.d.ts +15 -0
- package/dist/{hooks → react/hooks}/useOpenAiGlobal.d.ts +1 -1
- package/dist/react/hooks/useOpenExternal.d.ts +3 -0
- package/dist/{hooks → react/hooks}/useRequestDisplayMode.d.ts +1 -1
- package/dist/{hooks → react/hooks}/useToolEffect.d.ts +0 -4
- package/dist/react/hooks/useToolOutput.d.ts +1 -0
- package/dist/react/hooks/useToolResponseMetadata.d.ts +1 -0
- package/dist/react/hooks/useWidgetState.d.ts +4 -0
- package/dist/types/openai.d.ts +1 -2
- package/dist/vite/index.js +5 -0
- package/package.json +5 -1
- package/src/{apollo_client/client.ts → core/ApolloClient.ts} +21 -17
- package/src/{apollo_client/client.test.ts → core/__tests__/ApolloClient.test.ts} +8 -9
- package/src/index.ts +36 -11
- package/src/{apollo_client/provider.tsx → react/ApolloProvider.tsx} +12 -8
- package/src/{apollo_client/provider.test.tsx → react/__tests__/ApolloProvider.test.tsx} +9 -9
- package/src/react/context/ToolUseContext.tsx +30 -0
- package/src/{hooks → react/hooks/__tests__}/useCallTool.test.ts +1 -1
- package/src/{hooks → react/hooks/__tests__}/useOpenAiGlobal.test.ts +2 -2
- package/src/react/hooks/__tests__/useOpenExternal.test.tsx +24 -0
- package/src/{hooks → react/hooks/__tests__}/useRequestDisplayMode.test.ts +2 -2
- package/src/{hooks → react/hooks/__tests__}/useSendFollowUpMessage.test.ts +1 -1
- package/src/{hooks → react/hooks/__tests__}/useToolEffect.test.tsx +2 -1
- package/src/{hooks → react/hooks/__tests__}/useToolInput.test.ts +1 -1
- package/src/{hooks → react/hooks/__tests__}/useToolName.test.ts +1 -1
- package/src/react/hooks/__tests__/useToolOutput.test.tsx +49 -0
- package/src/react/hooks/__tests__/useToolResponseMetadata.test.tsx +49 -0
- package/src/react/hooks/__tests__/useWidgetState.test.tsx +158 -0
- package/src/{hooks → react/hooks}/useOpenAiGlobal.ts +4 -4
- package/src/react/hooks/useOpenExternal.ts +11 -0
- package/src/{hooks → react/hooks}/useRequestDisplayMode.ts +1 -1
- package/src/{hooks → react/hooks}/useToolEffect.tsx +3 -26
- package/src/{hooks → react/hooks}/useToolName.ts +1 -1
- package/src/react/hooks/useToolOutput.ts +5 -0
- package/src/react/hooks/useToolResponseMetadata.ts +5 -0
- package/src/react/hooks/useWidgetState.ts +48 -0
- package/src/testing/internal/index.ts +2 -0
- package/src/testing/internal/matchers/index.d.ts +9 -0
- package/src/testing/internal/matchers/index.ts +1 -0
- package/src/testing/internal/matchers/toRerender.ts +49 -0
- package/src/testing/internal/openai/dispatchStateChange.ts +9 -0
- package/src/testing/internal/openai/stubOpenAiGlobals.ts +13 -0
- package/src/types/openai.ts +1 -1
- package/src/vite/{absolute_asset_imports_plugin.test.ts → __tests__/absolute_asset_imports_plugin.test.ts} +1 -1
- package/src/vite/{application_manifest_plugin.test.ts → __tests__/application_manifest_plugin.test.ts} +33 -7
- package/src/vite/application_manifest_plugin.ts +6 -0
- package/vitest-setup.ts +1 -0
- package/dist/apollo_client/client.d.ts +0 -13
- package/dist/apollo_client/provider.d.ts +0 -5
- /package/dist/{apollo_client/link → link}/ToolCallLink.d.ts +0 -0
- /package/dist/{hooks → react/hooks}/useSendFollowUpMessage.d.ts +0 -0
- /package/dist/{hooks → react/hooks}/useToolInput.d.ts +0 -0
- /package/dist/{hooks → react/hooks}/useToolName.d.ts +0 -0
- /package/src/{apollo_client/link → link}/ToolCallLink.ts +0 -0
- /package/src/{hooks → react/hooks}/useCallTool.ts +0 -0
- /package/src/{hooks → react/hooks}/useSendFollowUpMessage.ts +0 -0
- /package/src/{hooks → react/hooks}/useToolInput.ts +0 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SetStateAction, useCallback, useState } from "react";
|
|
2
|
+
import { UnknownObject } from "../../types/openai";
|
|
3
|
+
import { useOpenAiGlobal } from "./useOpenAiGlobal";
|
|
4
|
+
|
|
5
|
+
export function useWidgetState<T extends UnknownObject>(
|
|
6
|
+
defaultState: T | (() => T)
|
|
7
|
+
): readonly [T, (state: SetStateAction<T>) => void];
|
|
8
|
+
|
|
9
|
+
export function useWidgetState<T extends UnknownObject>(
|
|
10
|
+
defaultState?: T | (() => T | null) | null
|
|
11
|
+
): readonly [T | null, (state: SetStateAction<T | null>) => void];
|
|
12
|
+
|
|
13
|
+
export function useWidgetState<T extends UnknownObject>(
|
|
14
|
+
defaultState?: T | (() => T | null) | null
|
|
15
|
+
): readonly [T | null, (state: SetStateAction<T | null>) => void] {
|
|
16
|
+
const widgetStateFromWindow = useOpenAiGlobal("widgetState") as T;
|
|
17
|
+
const [previousWidgetStateFromWindow, setPreviousWidgetStateFromWindow] =
|
|
18
|
+
useState(widgetStateFromWindow);
|
|
19
|
+
|
|
20
|
+
let [widgetState, _setWidgetState] = useState<T | null>(() => {
|
|
21
|
+
if (widgetStateFromWindow != null) {
|
|
22
|
+
return widgetStateFromWindow;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return typeof defaultState === "function" ? defaultState() : (
|
|
26
|
+
(defaultState ?? null)
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (previousWidgetStateFromWindow !== widgetStateFromWindow) {
|
|
31
|
+
_setWidgetState((widgetState = widgetStateFromWindow));
|
|
32
|
+
setPreviousWidgetStateFromWindow(widgetStateFromWindow);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const setWidgetState = useCallback((state: SetStateAction<T | null>) => {
|
|
36
|
+
_setWidgetState((prevState) => {
|
|
37
|
+
const newState = typeof state === "function" ? state(prevState) : state;
|
|
38
|
+
|
|
39
|
+
if (newState != null && typeof window !== "undefined") {
|
|
40
|
+
void window.openai?.setWidgetState?.(newState);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return newState;
|
|
44
|
+
});
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
return [widgetState, setWidgetState];
|
|
48
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { NextRenderOptions } from "@testing-library/react-render-stream";
|
|
2
|
+
|
|
3
|
+
interface CustomMatchers<R = unknown> {
|
|
4
|
+
toRerender: (options?: NextRenderOptions) => Promise<R>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare module "vitest" {
|
|
8
|
+
interface Assertion<T = any> extends CustomMatchers<T> {}
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./toRerender";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Vitest port of toRerender from
|
|
2
|
+
// https://github.com/testing-library/react-render-stream-testing-library/blob/main/src/expect/renderStreamMatchers.ts
|
|
3
|
+
import {
|
|
4
|
+
Assertable,
|
|
5
|
+
NextRenderOptions,
|
|
6
|
+
RenderStream,
|
|
7
|
+
WaitForRenderTimeoutError,
|
|
8
|
+
} from "@testing-library/react-render-stream";
|
|
9
|
+
|
|
10
|
+
import { expect } from "vitest";
|
|
11
|
+
|
|
12
|
+
const assertableSymbol = Symbol.for(
|
|
13
|
+
"@testing-library/react-render-stream:assertable"
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
expect.extend({
|
|
17
|
+
async toRerender(actual, options: NextRenderOptions) {
|
|
18
|
+
const _stream = actual as RenderStream<any> | Assertable;
|
|
19
|
+
const stream = (
|
|
20
|
+
assertableSymbol in _stream ?
|
|
21
|
+
_stream[assertableSymbol]
|
|
22
|
+
: _stream) as RenderStream<any>;
|
|
23
|
+
const hint = this.utils.matcherHint("toRerender", undefined, undefined, {
|
|
24
|
+
isNot: this.isNot,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
let pass = true;
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
await stream.peekRender({ timeout: 100, ...options });
|
|
31
|
+
} catch (e) {
|
|
32
|
+
if (e instanceof WaitForRenderTimeoutError) {
|
|
33
|
+
pass = false;
|
|
34
|
+
} else {
|
|
35
|
+
throw e;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
pass,
|
|
41
|
+
message() {
|
|
42
|
+
return (
|
|
43
|
+
`${hint}\n\nExpected component to${pass ? " not" : ""} rerender, ` +
|
|
44
|
+
`but it did${pass ? "" : " not"}.`
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { vi } from "vitest";
|
|
2
|
+
import { API, OpenAiGlobals, UnknownObject } from "../../../types/openai";
|
|
3
|
+
import { dispatchStateChange } from "./dispatchStateChange";
|
|
4
|
+
|
|
5
|
+
export function stubOpenAiGlobals(globals?: Partial<API<any> & OpenAiGlobals>) {
|
|
6
|
+
vi.stubGlobal("openai", {
|
|
7
|
+
setWidgetState: (state: UnknownObject) => {
|
|
8
|
+
window.openai.widgetState = state;
|
|
9
|
+
dispatchStateChange();
|
|
10
|
+
},
|
|
11
|
+
...globals,
|
|
12
|
+
});
|
|
13
|
+
}
|
package/src/types/openai.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test, vi, describe, beforeEach, Mock } from "vitest";
|
|
2
|
-
import { AbsoluteAssetImportsPlugin } from "
|
|
2
|
+
import { AbsoluteAssetImportsPlugin } from "../absolute_asset_imports_plugin";
|
|
3
3
|
|
|
4
4
|
test("Should replace root relative scripts with full url when origin is provided", () => {
|
|
5
5
|
const ctx = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test, vi, describe, beforeEach, Mock } from "vitest";
|
|
2
|
-
import { ApplicationManifestPlugin } from "
|
|
2
|
+
import { ApplicationManifestPlugin } from "../application_manifest_plugin";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import * as glob from "glob";
|
|
5
5
|
import path from "path";
|
|
@@ -562,6 +562,32 @@ describe("buildStart", () => {
|
|
|
562
562
|
);
|
|
563
563
|
});
|
|
564
564
|
|
|
565
|
+
test("Should error when tool name contains spaces", async () => {
|
|
566
|
+
vi.spyOn(fs, "readFileSync").mockImplementation((path) => {
|
|
567
|
+
if (path === "package.json") {
|
|
568
|
+
return JSON.stringify({});
|
|
569
|
+
} else if (path === "my-component.tsx") {
|
|
570
|
+
return `
|
|
571
|
+
const MY_QUERY = gql\`query HelloWorldQuery @tool(name: "hello world", description: "A tool") { helloWorld }\`;
|
|
572
|
+
`;
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
vi.spyOn(glob, "glob").mockImplementation(() =>
|
|
576
|
+
Promise.resolve(["my-component.tsx"])
|
|
577
|
+
);
|
|
578
|
+
vi.spyOn(path, "resolve").mockImplementation((_, file) => file);
|
|
579
|
+
vi.spyOn(fs, "writeFileSync");
|
|
580
|
+
|
|
581
|
+
const plugin = ApplicationManifestPlugin();
|
|
582
|
+
plugin.configResolved({ command: "serve", server: {} });
|
|
583
|
+
|
|
584
|
+
await expect(
|
|
585
|
+
async () => await plugin.buildStart()
|
|
586
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
587
|
+
`[Error: Tool with name "hello world" contains spaces which is not allowed.]`
|
|
588
|
+
);
|
|
589
|
+
});
|
|
590
|
+
|
|
565
591
|
test("Should error when tool name is not a string", async () => {
|
|
566
592
|
vi.spyOn(fs, "readFileSync").mockImplementation((path) => {
|
|
567
593
|
if (path === "package.json") {
|
|
@@ -675,16 +701,16 @@ describe("buildStart", () => {
|
|
|
675
701
|
} else if (path === root + "/my-component.tsx") {
|
|
676
702
|
return `
|
|
677
703
|
const MY_QUERY = gql\`
|
|
678
|
-
fragment A on User { firstName }
|
|
679
|
-
fragment B on User { lastName }
|
|
680
|
-
query HelloWorldQuery @tool(name: "hello-world", description: "This is an awesome tool!") {
|
|
704
|
+
fragment A on User { firstName }
|
|
705
|
+
fragment B on User { lastName }
|
|
706
|
+
query HelloWorldQuery @tool(name: "hello-world", description: "This is an awesome tool!") {
|
|
681
707
|
helloWorld {
|
|
682
708
|
...B
|
|
683
709
|
...A
|
|
684
710
|
...C
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
fragment C on User { middleName }
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
fragment C on User { middleName }
|
|
688
714
|
}\`;
|
|
689
715
|
`;
|
|
690
716
|
}
|
|
@@ -140,6 +140,12 @@ export const ApplicationManifestPlugin = () => {
|
|
|
140
140
|
throw new Error("'name' argument must be supplied for @tool");
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
if (name.indexOf(" ") > -1) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Tool with name "${name}" contains spaces which is not allowed.`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
143
149
|
if (!description) {
|
|
144
150
|
throw new Error(
|
|
145
151
|
"'description' argument must be supplied for @tool"
|
package/vitest-setup.ts
CHANGED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { ApolloClient } from "@apollo/client";
|
|
2
|
-
import "../types/openai";
|
|
3
|
-
import { ApplicationManifest } from "../types/application-manifest";
|
|
4
|
-
type ExtendedApolloClientOptions = Omit<ApolloClient.Options, "link"> & {
|
|
5
|
-
link?: ApolloClient.Options["link"];
|
|
6
|
-
manifest: ApplicationManifest;
|
|
7
|
-
};
|
|
8
|
-
export declare class ExtendedApolloClient extends ApolloClient {
|
|
9
|
-
manifest: ApplicationManifest;
|
|
10
|
-
constructor(options: ExtendedApolloClientOptions);
|
|
11
|
-
prefetchData(): Promise<void>;
|
|
12
|
-
}
|
|
13
|
-
export {};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|