@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.
Files changed (63) hide show
  1. package/.github/workflows/compare-build-output.yml +28 -0
  2. package/config/compare-build-output-to.sh +90 -0
  3. package/dist/core/ApolloClient.d.ts +14 -0
  4. package/dist/index.d.ts +17 -11
  5. package/dist/index.js +96 -44
  6. package/dist/react/ApolloProvider.d.ts +9 -0
  7. package/dist/react/context/ToolUseContext.d.ts +15 -0
  8. package/dist/{hooks → react/hooks}/useOpenAiGlobal.d.ts +1 -1
  9. package/dist/react/hooks/useOpenExternal.d.ts +3 -0
  10. package/dist/{hooks → react/hooks}/useRequestDisplayMode.d.ts +1 -1
  11. package/dist/{hooks → react/hooks}/useToolEffect.d.ts +0 -4
  12. package/dist/react/hooks/useToolOutput.d.ts +1 -0
  13. package/dist/react/hooks/useToolResponseMetadata.d.ts +1 -0
  14. package/dist/react/hooks/useWidgetState.d.ts +4 -0
  15. package/dist/types/openai.d.ts +1 -2
  16. package/dist/vite/index.js +5 -0
  17. package/package.json +5 -1
  18. package/src/{apollo_client/client.ts → core/ApolloClient.ts} +21 -17
  19. package/src/{apollo_client/client.test.ts → core/__tests__/ApolloClient.test.ts} +8 -9
  20. package/src/index.ts +36 -11
  21. package/src/{apollo_client/provider.tsx → react/ApolloProvider.tsx} +12 -8
  22. package/src/{apollo_client/provider.test.tsx → react/__tests__/ApolloProvider.test.tsx} +9 -9
  23. package/src/react/context/ToolUseContext.tsx +30 -0
  24. package/src/{hooks → react/hooks/__tests__}/useCallTool.test.ts +1 -1
  25. package/src/{hooks → react/hooks/__tests__}/useOpenAiGlobal.test.ts +2 -2
  26. package/src/react/hooks/__tests__/useOpenExternal.test.tsx +24 -0
  27. package/src/{hooks → react/hooks/__tests__}/useRequestDisplayMode.test.ts +2 -2
  28. package/src/{hooks → react/hooks/__tests__}/useSendFollowUpMessage.test.ts +1 -1
  29. package/src/{hooks → react/hooks/__tests__}/useToolEffect.test.tsx +2 -1
  30. package/src/{hooks → react/hooks/__tests__}/useToolInput.test.ts +1 -1
  31. package/src/{hooks → react/hooks/__tests__}/useToolName.test.ts +1 -1
  32. package/src/react/hooks/__tests__/useToolOutput.test.tsx +49 -0
  33. package/src/react/hooks/__tests__/useToolResponseMetadata.test.tsx +49 -0
  34. package/src/react/hooks/__tests__/useWidgetState.test.tsx +158 -0
  35. package/src/{hooks → react/hooks}/useOpenAiGlobal.ts +4 -4
  36. package/src/react/hooks/useOpenExternal.ts +11 -0
  37. package/src/{hooks → react/hooks}/useRequestDisplayMode.ts +1 -1
  38. package/src/{hooks → react/hooks}/useToolEffect.tsx +3 -26
  39. package/src/{hooks → react/hooks}/useToolName.ts +1 -1
  40. package/src/react/hooks/useToolOutput.ts +5 -0
  41. package/src/react/hooks/useToolResponseMetadata.ts +5 -0
  42. package/src/react/hooks/useWidgetState.ts +48 -0
  43. package/src/testing/internal/index.ts +2 -0
  44. package/src/testing/internal/matchers/index.d.ts +9 -0
  45. package/src/testing/internal/matchers/index.ts +1 -0
  46. package/src/testing/internal/matchers/toRerender.ts +49 -0
  47. package/src/testing/internal/openai/dispatchStateChange.ts +9 -0
  48. package/src/testing/internal/openai/stubOpenAiGlobals.ts +13 -0
  49. package/src/types/openai.ts +1 -1
  50. package/src/vite/{absolute_asset_imports_plugin.test.ts → __tests__/absolute_asset_imports_plugin.test.ts} +1 -1
  51. package/src/vite/{application_manifest_plugin.test.ts → __tests__/application_manifest_plugin.test.ts} +33 -7
  52. package/src/vite/application_manifest_plugin.ts +6 -0
  53. package/vitest-setup.ts +1 -0
  54. package/dist/apollo_client/client.d.ts +0 -13
  55. package/dist/apollo_client/provider.d.ts +0 -5
  56. /package/dist/{apollo_client/link → link}/ToolCallLink.d.ts +0 -0
  57. /package/dist/{hooks → react/hooks}/useSendFollowUpMessage.d.ts +0 -0
  58. /package/dist/{hooks → react/hooks}/useToolInput.d.ts +0 -0
  59. /package/dist/{hooks → react/hooks}/useToolName.d.ts +0 -0
  60. /package/src/{apollo_client/link → link}/ToolCallLink.ts +0 -0
  61. /package/src/{hooks → react/hooks}/useCallTool.ts +0 -0
  62. /package/src/{hooks → react/hooks}/useSendFollowUpMessage.ts +0 -0
  63. /package/src/{hooks → react/hooks}/useToolInput.ts +0 -0
@@ -0,0 +1,5 @@
1
+ import { useOpenAiGlobal } from "./useOpenAiGlobal";
2
+
3
+ export function useToolResponseMetadata() {
4
+ return useOpenAiGlobal("toolResponseMetadata") ?? null;
5
+ }
@@ -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,2 @@
1
+ export { dispatchStateChange } from "./openai/dispatchStateChange";
2
+ export { stubOpenAiGlobals } from "./openai/stubOpenAiGlobals";
@@ -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,9 @@
1
+ import { SET_GLOBALS_EVENT_TYPE } from "../../../types/openai";
2
+
3
+ export function dispatchStateChange() {
4
+ window.dispatchEvent(
5
+ new CustomEvent(SET_GLOBALS_EVENT_TYPE, {
6
+ detail: { globals: window.openai },
7
+ })
8
+ );
9
+ }
@@ -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
+ }
@@ -1,4 +1,4 @@
1
- type UnknownObject = any;
1
+ export type UnknownObject = Record<string, unknown>;
2
2
 
3
3
  declare global {
4
4
  interface Window {
@@ -1,5 +1,5 @@
1
1
  import { expect, test, vi, describe, beforeEach, Mock } from "vitest";
2
- import { AbsoluteAssetImportsPlugin } from "./absolute_asset_imports_plugin";
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 "./application_manifest_plugin";
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 +1,2 @@
1
1
  import "@testing-library/jest-dom/vitest";
2
+ import "./src/testing/internal/matchers";
@@ -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 {};
@@ -1,5 +0,0 @@
1
- import React from "react";
2
- import { ExtendedApolloClient } from "./client";
3
- export declare const ExtendedApolloProvider: ({ children, client, }: React.PropsWithChildren<{
4
- client: ExtendedApolloClient;
5
- }>) => React.JSX.Element;
File without changes
File without changes
File without changes
File without changes