├── .dockerignore
├── .env.example
├── .eslintrc.json
├── .gitignore
├── .husky
├── pre-commit
└── pre-push
├── .prettierignore
├── .prettierrc
├── LICENSE
├── Makefile
├── README.md
├── __tests__
├── api
│ ├── parse-html.test.ts
│ ├── replacePlaceholdersDuringStreaming.test.ts
│ └── streamResponseToUser.test.ts
├── apiMock
│ ├── chunksToProperties.test.ts
│ ├── getMockedProperties.test.ts
│ ├── removePropertiesItems.test.ts
│ └── slug.test.ts
├── lib
│ ├── actionUtils.test.ts
│ ├── edge-runtime
│ │ ├── angelaUtils.test.ts
│ │ ├── apiResponseSimplification.test.ts
│ │ ├── dataAnalysis.test.ts
│ │ ├── removeOldestFunctionCalls.test.ts
│ │ └── utils.test.ts
│ ├── embed-docs
│ │ ├── getUrlText.test.ts
│ │ ├── splitIntoTextChunks.test.ts
│ │ └── utils.test.ts
│ ├── parsers
│ │ ├── parseDataAnalysisResponse.test.ts
│ │ └── parsers.test.ts
│ ├── v2
│ │ ├── edge-runtime
│ │ │ ├── addAssistantHistory.test.ts
│ │ │ ├── ai.test.ts
│ │ │ ├── dataAnalysis.test.ts
│ │ │ └── filter-actions.test.ts
│ │ └── prompts
│ │ │ ├── clarification.test.ts
│ │ │ ├── dataAnalysis.test.ts
│ │ │ ├── parseGeneratedCode.test.ts
│ │ │ ├── routing.test.ts
│ │ │ ├── summariseChatHistory.test.ts
│ │ │ └── utils.test.ts
│ └── v3
│ │ ├── prompts_parsers
│ │ ├── explanation.test.ts
│ │ └── matching.test.ts
│ │ └── utils.test.ts
├── missingParamCorrect.test.ts
├── postprocessResponse.test.ts
├── prompts
│ ├── analysisPrompt.test.ts
│ ├── apiMockPrompt.test.ts
│ ├── getActionDescriptions.test.ts
│ ├── reqBodyToTS.test.ts
│ ├── requestCorrectionPrompt.test.ts
│ ├── suggestFollowUps.test.ts
│ ├── summarizeText.test.ts
│ └── testData.ts
├── requests.test.ts
├── swagger-to-actions.test.ts
├── testActions.ts
├── testData
│ ├── Henry-Pulver-CV.pdf
│ ├── LOA_superflows-sign-in_413.pdf
│ ├── jira-openapi-spec.json
│ ├── linuxReddit.json
│ ├── localtest-openapi-spec.json
│ ├── openai-openapi-spec.json
│ ├── pokemon.json
│ ├── posthog-openapi-spec.json
│ ├── sentry-openapi-spec.json
│ ├── sf2.json
│ ├── supabase-openapi-spec.json
│ └── superflows-openapi.json
└── utils
│ ├── chunkString.test.ts
│ ├── isDate.test.ts
│ ├── isID.test.ts
│ ├── isURL.test.ts
│ ├── joinArraysNoDuplicates.test.ts
│ ├── jsonSplitter.test.ts
│ └── stripTrailingAndCurly.test.ts
├── components
├── actions
│ ├── APITabs.tsx
│ ├── actionsSection.tsx
│ ├── consts.ts
│ ├── editActionModal.tsx
│ ├── editActionTagModal.tsx
│ ├── uploadModal.tsx
│ └── viewPromptModal.tsx
├── approval
│ ├── addQuestionModal.tsx
│ ├── editModals.tsx
│ ├── followUps.tsx
│ ├── question.tsx
│ ├── types.ts
│ └── verifyAnswerScreen.tsx
├── autoGrowingTextarea.tsx
├── checkbox.tsx
├── combobox.tsx
├── contextManagers
│ └── profile.tsx
├── dropdown.tsx
├── floatingLabelInput.tsx
├── flyout.tsx
├── flyoutMenu.tsx
├── getServerSideProps.ts
├── headers.tsx
├── icons.tsx
├── loadingspinner.tsx
├── modal.tsx
├── navbar.tsx
├── onboarding
│ ├── confirmAuth.tsx
│ ├── progressBar.tsx
│ └── uploadSpec.tsx
├── paginationPageSelector.tsx
├── playground.tsx
├── playgroundChatbot.tsx
├── selectBox.tsx
├── signIn.tsx
├── toggle.tsx
├── transcripts
│ └── transcriptSearchSidebar.tsx
└── warningModal.tsx
├── docker
└── development
│ ├── Dockerfile
│ ├── README.md
│ ├── docker-compose.yml
│ ├── init-windows.ps1
│ └── init.sh
├── global-jest-setup.ts
├── jest.config.js
├── lib
├── actionUtils.ts
├── apiKey.ts
├── builtinActions.ts
├── consts.ts
├── database.types.ts
├── edge-runtime
│ ├── ai.ts
│ ├── angelaUtils.ts
│ ├── apiResponseSimplification.ts
│ ├── dataAnalysis.ts
│ ├── filterActions.ts
│ ├── llmResponseCache.ts
│ ├── missingParamCorrection.ts
│ ├── requests.ts
│ ├── summarize.ts
│ └── utils.ts
├── embed-docs
│ ├── docsSearch.ts
│ ├── embedDocs.ts
│ ├── embedText.ts
│ ├── getNestedUrls.ts
│ └── utils.ts
├── funLoadingMessages.ts
├── hooks
│ └── useDocumentsLoader.ts
├── language.ts
├── models.ts
├── parsers
│ ├── dataAnalysis.ts
│ └── parsers.ts
├── prompts
│ ├── actionFiltering.ts
│ ├── apiMock.ts
│ ├── chatBot.ts
│ ├── dataAnalysis.ts
│ ├── hallucinateDocs.ts
│ ├── requestCorrection.ts
│ ├── suggestFollowUps.ts
│ ├── summarizeText.ts
│ └── tsConversion.ts
├── queryLLM.ts
├── types.ts
├── utils.ts
├── v2
│ ├── builtinActions.ts
│ ├── edge-runtime
│ │ ├── ai.ts
│ │ ├── clarification.ts
│ │ ├── dataAnalysis.ts
│ │ ├── filterActions.ts
│ │ ├── summariseChatHistory.ts
│ │ └── utils.ts
│ └── prompts
│ │ ├── actionFiltering.ts
│ │ ├── chatBot.ts
│ │ ├── clarification.ts
│ │ ├── dataAnalysis.ts
│ │ ├── explainNotPossible.ts
│ │ ├── routing.ts
│ │ ├── summariseChatHistory.ts
│ │ ├── utils.ts
│ │ └── writeActionDescription.ts
└── v3
│ ├── edge-runtime
│ ├── ai.ts
│ ├── dataAnalysis.ts
│ └── matching.ts
│ ├── prompts_parsers
│ ├── chatToDocs.ts
│ ├── codeGen.ts
│ ├── descriptionGeneration.ts
│ ├── explanation.ts
│ ├── filtering.ts
│ ├── generateAlternativeQuestions.ts
│ ├── matching.ts
│ ├── routing.ts
│ └── utils.ts
│ └── utils.ts
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── pages
├── _app.tsx
├── _error.tsx
├── actions.tsx
├── ai-settings.tsx
├── analytics.tsx
├── api-settings.tsx
├── api
│ ├── create-org.ts
│ ├── embed-questions.ts
│ ├── embed-text.ts
│ ├── generate-alternative-questions.ts
│ ├── join-org.ts
│ ├── mock
│ │ └── [...slug].ts
│ ├── parse-html.ts
│ ├── search-docs.ts
│ ├── swagger-to-actions.ts
│ ├── team.ts
│ ├── v1
│ │ ├── answers.ts
│ │ ├── confirm.ts
│ │ ├── feedback.ts
│ │ └── follow-ups.ts
│ ├── v3
│ │ ├── generate-answer-description.ts
│ │ └── generate-answer-offline.ts
│ └── write-action-descriptions.ts
├── approval
│ ├── [id].tsx
│ └── index.tsx
├── chat-to-docs.tsx
├── index.tsx
├── onboarding.tsx
├── project.tsx
├── sign-in.tsx
├── team.tsx
└── usage.tsx
├── postcss.config.js
├── public
├── crm-ai.png
├── favicon.png
├── google.svg
├── modal.png
├── sf-actions-corner.png
├── sf-actions.png
├── sf-crm-ai-corner.png
├── sf-logo-long.png
├── superflows-in-action.gif
└── superflows-sidebar.gif
├── sentry.client.config.ts
├── sentry.edge.config.ts
├── sentry.server.config.ts
├── styles
└── globals.css
├── supabase
├── .gitignore
├── config.toml
├── functions
│ ├── execute-code-2
│ │ ├── actionUtils.ts
│ │ ├── index.ts
│ │ ├── requests.ts
│ │ ├── types.ts
│ │ └── utils.ts
│ └── execute-code
│ │ └── index.ts
├── migrations
│ ├── 20230619150835_initialize_tables.sql
│ ├── 20230627202036_tweak-orgs.sql
│ ├── 20230703134143_add-usage-table.sql
│ ├── 20230704155118_usage-limit-free-tier.sql
│ ├── 20230705131234_add-endpoint-postprocessing.sql
│ ├── 20230713135915_groups-to-tags.sql
│ ├── 20230718120522_support-auth-methods.sql
│ ├── 20230719101013_email-password-login.sql
│ ├── 20230720103603_reply-in-same-language.sql
│ ├── 20230721175829_add-team-page.sql
│ ├── 20230724092452_enable-preset-api-specs.sql
│ ├── 20230801125929_add_num_user_queries.sql
│ ├── 20230811160337_enable-multiple-apis-per-project.sql
│ ├── 20230824170129_add-summaries-to-chat-messages.sql
│ ├── 20230905191132_add-finetuned-models.sql
│ ├── 20230908092648_nonunique-finetuned-models.sql
│ ├── 20230911140113_allow-setting-llm.sql
│ ├── 20230913160910_add-fixed-headers.sql
│ ├── 20230913183248_add_feedback_table.sql
│ ├── 20230927100441_allow-query-param-auth.sql
│ ├── 20230929150231_sanitize-urls-or-ids-first.sql
│ ├── 20230929160459_enable-disable-confirmation-per-action.sql
│ ├── 20231010172954_enable-setting-language.sql
│ ├── 20231015163802_add-embedded-docs-table.sql
│ ├── 20231024124328_add-chatbot-instructions.sql
│ ├── 20231025130024_add-action-links.sql
│ ├── 20231031180334_drop-auth-header-constraint.sql
│ ├── 20231101022950_add-doc_chunks-queries.sql
│ ├── 20231129102947_add-analytics.sql
│ ├── 20231214175242_add-caching-feature-flag.sql
│ ├── 20231219180935_caching-2.sql
│ ├── 20240111123041_bertie-ai-v2.sql
│ ├── 20240304093715_cascade-from-conversations.sql
│ ├── 20240314095402_auditable_logs.sql
│ ├── 20240316165535_caching_bertie.sql
│ ├── 20240411111508_fun-loading-messages.sql
│ ├── 20240415100158_add-data-analysis-flag.sql
│ ├── 20240416162029_cassius.sql
│ ├── 20240422173420_fix-issue-in-functions.sql
│ ├── 20240422200717_fix-issue-in-functions-2.sql
│ ├── 20240512202947_add-disable-direct-flag.sql
│ ├── 20240515124621_add-analytics-page.sql
│ ├── 20240518213300_approval-column.sql
│ ├── 20240520165609_unique-fnNames.sql
│ ├── 20240527132119_cassius-revamped-docs.sql
│ ├── 20240605105600_store-variables-for-cache-correctly.sql
│ ├── 20240606135049_add-user-id.sql
│ └── 20240613150006_fix_chat_to_docs_page.sql
└── seed.sql
├── tailwind.config.js
├── tsconfig.json
└── update-types.sh
/.dockerignore:
--------------------------------------------------------------------------------
1 | .env
2 | docker/*
3 | .git
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # Supabase
2 | NEXT_PUBLIC_SUPABASE_URL=http://localhost:8000
3 | NEXT_PUBLIC_SUPABASE_ANON_KEY=
4 | SERVICE_LEVEL_KEY_SUPABASE=
5 | SUPERFLOWS_PORT=8080
6 | POSTGRES_USER=postgres
7 | # AI
8 | OPENAI_API_KEY=
9 |
10 | # (Optional) Google
11 | GOOGLE_APP_CLIENT_ID=
12 | GOOGLE_APP_CLIENT_SECRET=
13 |
14 | # (Optional) Sentry
15 | SENTRY_AUTH_TOKEN=
16 | NEXT_PUBLIC_SENTRY_DSN=
17 | SENTRY_PROJECT=
18 | SENTRY_ORG=
19 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals",
3 | "rules": {
4 | "@next/next/no-html-link-for-pages": "off"
5 | },
6 | "ignorePatterns": ["docker/development/supabase"]
7 | }
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 |
132 | # Sentry Auth Token
133 | .sentryclirc
134 |
135 | # Custom
136 | .vscode/
137 | local/
138 | .idea/
139 | install-sidebar.sh
140 | .vercel
141 | docker/development/supabase/
142 | # Sentry Config File
143 | .sentryclirc
144 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npm run lint
5 | npx pretty-quick --staged --fix
6 |
--------------------------------------------------------------------------------
/.husky/pre-push:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npm run testprepush
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | .next
3 | node_modules/
4 | tsconfig.json
5 | tailwind.config.js
6 | .eslintrc.json
7 | next.config.js
8 | next-env.d.ts
9 | package.json
10 | postcss.config.js
11 | README.md
12 | sentry.*
13 | styles/globals.css
14 | supabase/.temp/
15 | __tests__/testData/
16 | docker/development/
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all"
3 | }
4 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all build clean
2 |
3 | p:
4 | npx prettier --write . --check '!./docker/development/supabase/docker/volumes'
5 |
6 | pc: p
7 | npm run lint
8 |
9 | run:
10 | npm run dev
11 |
12 | types:
13 | ./update-types.sh
14 |
15 | build:
16 | npm run build
17 |
18 | test:
19 | npm test
20 |
--------------------------------------------------------------------------------
/__tests__/api/parse-html.test.ts:
--------------------------------------------------------------------------------
1 | import { removeHiddenElements } from "../../pages/api/parse-html";
2 | import * as cheerio from "cheerio";
3 |
4 | describe("removeHiddenElements", () => {
5 | const allversions = [
6 | 'style="display:none"',
7 | 'style="display: none"',
8 | 'style="display:none;"',
9 | 'style="display: none;"',
10 | "style='display:none'",
11 | "style='display: none'",
12 | "style='display:none;'",
13 | "style='display: none;'",
14 | ];
15 | it("should remove hidden elements", () => {
16 | allversions.forEach((style) => {
17 | const cheerioInput = cheerio
18 | .load(
19 | `
Something that's visible
`,
20 | )
21 | .root()
22 | .find("body");
23 | const out = removeHiddenElements(cheerioInput);
24 | expect(out.html()).toEqual("Something that's visible
");
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/__tests__/api/streamResponseToUser.test.ts:
--------------------------------------------------------------------------------
1 | import { Readable } from "stream";
2 | import { streamResponseToUser } from "../../lib/edge-runtime/angelaUtils";
3 |
4 | describe("streamResponseToUser", () => {
5 | it("very simple", async () => {
6 | const mockReadable = new Readable();
7 | mockReadable.push('data: {"choices": [{"delta": {"content": "hello"}}]}');
8 | mockReadable.push(null);
9 |
10 | const uint8array = new TextEncoder().encode(mockReadable.read());
11 | const mockStream = new ReadableStream({
12 | start(controller) {
13 | controller.enqueue(uint8array);
14 | controller.close();
15 | },
16 | });
17 | const mockFn = jest.fn();
18 | // @ts-ignore
19 | const out = await streamResponseToUser(mockStream, mockFn, {});
20 | expect(out).toEqual("hello");
21 | expect(mockFn).toBeCalledWith({ role: "assistant", content: "hello" });
22 | });
23 | it("replace ID1 with original id correctly", async () => {
24 | const mockReadable = new Readable();
25 | mockReadable.push(
26 | 'data: {"choices": [{"delta": {"content": "The id is"}}]}',
27 | );
28 | mockReadable.push('data: {"choices": [{"delta": {"content": " ID"}}]}');
29 | mockReadable.push('data: {"choices": [{"delta": {"content": "1 "}}]}');
30 | mockReadable.push(null); // end the stream
31 |
32 | const uint8array = new TextEncoder().encode(mockReadable.read());
33 | const mockStream = new ReadableStream({
34 | start(controller) {
35 | controller.enqueue(uint8array);
36 | controller.close();
37 | },
38 | });
39 | const mockFn = jest.fn();
40 | // @ts-ignore
41 | const out = await streamResponseToUser(mockStream, mockFn, {
42 | "53fa4-3f3f3-3f3f3-3f3f3-3f3f3": "ID1",
43 | });
44 | expect(out).toEqual("The id is ID1 ");
45 | expect(mockFn).toBeCalledWith({ role: "assistant", content: "The id is" });
46 | expect(mockFn).toBeCalledWith({
47 | role: "assistant",
48 | content: " 53fa4-3f3f3-3f3f3-3f3f3-3f3f3 ",
49 | });
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/__tests__/apiMock/getMockedProperties.test.ts:
--------------------------------------------------------------------------------
1 | import "jest";
2 | import { getLLMResponse } from "../../lib/queryLLM";
3 | import { getMockedProperties } from "../../pages/api/mock/[...slug]";
4 |
5 | jest.mock("../../lib/queryLLM");
6 |
7 | // This resets the number of times called counts between tests
8 | afterEach(() => {
9 | jest.clearAllMocks();
10 | });
11 | describe("getMockedProperties", () => {
12 | it("simple test case", async () => {
13 | const openAiResponse = `
14 | firstName: John
15 | lastName: Smith
16 | `;
17 |
18 | const openApiProperties = {
19 | firstName: {
20 | type: "string",
21 | nullable: false,
22 | },
23 | lastName: {
24 | type: "string",
25 | nullable: false,
26 | },
27 | };
28 |
29 | (getLLMResponse as jest.Mock).mockReturnValue(openAiResponse);
30 |
31 | const res = await getMockedProperties(
32 | openApiProperties,
33 | "/api/v1/Status/fullname/123",
34 | "GET",
35 | [
36 | {
37 | path: ["id"],
38 | data: "The user's id",
39 | },
40 | ],
41 | );
42 | expect(res).toEqual({ firstName: "John", lastName: "Smith" });
43 | });
44 |
45 | it("with array", async () => {
46 | const openApiProperties = {
47 | id: {
48 | type: "string",
49 | },
50 | name: {
51 | type: "integer",
52 | },
53 | };
54 |
55 | const openAiResponse = `
56 | id: [1,2,3]
57 | name: ['John', 'Eric', 'Martin']
58 | `;
59 |
60 | const expected = [
61 | {
62 | id: 1,
63 | name: "John",
64 | },
65 | {
66 | id: 2,
67 | name: "Eric",
68 | },
69 | {
70 | id: 3,
71 | name: "Martin",
72 | },
73 | ];
74 |
75 | (getLLMResponse as jest.Mock).mockReturnValue(openAiResponse);
76 |
77 | const res = await getMockedProperties(
78 | openApiProperties,
79 | "/api/v1/Status/fullname/123",
80 | "GET",
81 | null,
82 | undefined,
83 | true,
84 | );
85 | expect(res).toEqual(expected);
86 | });
87 | it("nested object", async () => {
88 | // TODO: remove properties from here
89 | // learney.atlassian.net/browse/SF-2007
90 | const openAiResponse = `
91 | browserSdkVersion: 1
92 | dateCreated: 2021-01-01
93 | dsn.properties.cdn: nice-cdn
94 | dsn.properties.csp: nice-csp
95 | `;
96 |
97 | const properties = {
98 | browserSdkVersion: {
99 | type: "string",
100 | },
101 | dateCreated: {
102 | type: "string",
103 | },
104 | dsn: {
105 | type: "object",
106 | properties: {
107 | cdn: {
108 | type: "string",
109 | },
110 | csp: {
111 | type: "string",
112 | },
113 | },
114 | },
115 | };
116 |
117 | (getLLMResponse as jest.Mock).mockReturnValue(openAiResponse);
118 |
119 | const expectedOutput = {
120 | browserSdkVersion: 1,
121 | dateCreated: "2021-01-01",
122 | dsn: { cdn: "nice-cdn", csp: "nice-csp" },
123 | };
124 |
125 | const res = await getMockedProperties(
126 | properties,
127 | "/api/v1/Status/fullname/123",
128 | "GET",
129 | [
130 | {
131 | path: ["id"],
132 | data: "The user's id",
133 | },
134 | ],
135 | );
136 |
137 | expect(res).toEqual(expectedOutput);
138 | });
139 | });
140 |
--------------------------------------------------------------------------------
/__tests__/apiMock/removePropertiesItems.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from "@jest/globals";
2 | import { removePropertiesItems } from "../../pages/api/mock/[...slug]";
3 |
4 | describe("removePropertiesItems", () => {
5 | const expected = { out: 1, nowt: 2 };
6 | it("does nothing to nice output", () => {
7 | const out = removePropertiesItems(expected);
8 | expect(out).toEqual(expected);
9 | });
10 | it("nested in properties", () => {
11 | const out = removePropertiesItems({ properties: expected });
12 | expect(out).toEqual(expected);
13 | });
14 | it("nested in items", () => {
15 | const out = removePropertiesItems({ items: expected });
16 | expect(out).toEqual(expected);
17 | });
18 | it("double nested", () => {
19 | const out = removePropertiesItems({ properties: { items: expected } });
20 | expect(out).toEqual(expected);
21 | });
22 | it("double nested with 1 other entry", () => {
23 | const out = removePropertiesItems({
24 | properties: { items: expected },
25 | another: "one",
26 | });
27 | expect(out).toEqual({ ...expected, another: "one" });
28 | });
29 | it("double nested with multiple other entries", () => {
30 | const out = removePropertiesItems({
31 | properties: { items: expected, andAnother: "one" },
32 | another: "one",
33 | });
34 | expect(out).toEqual({ ...expected, another: "one", andAnother: "one" });
35 | });
36 | it("double nested with inner non-removed key", () => {
37 | const out = removePropertiesItems({
38 | properties: { key: expected, andAnother: "one" },
39 | another: "one",
40 | });
41 | expect(out).toEqual({ key: expected, another: "one", andAnother: "one" });
42 | });
43 | it("double nested with outer non-removed key", () => {
44 | const out = removePropertiesItems({
45 | key: { items: expected, andAnother: "one" },
46 | another: "one",
47 | });
48 | expect(out).toEqual({
49 | key: { ...expected, andAnother: "one" },
50 | another: "one",
51 | });
52 | });
53 | it("array of objects", () => {
54 | const out = removePropertiesItems([
55 | { items: expected },
56 | { items: expected },
57 | { items: expected },
58 | ]);
59 | expect(out).toEqual([expected, expected, expected]);
60 | });
61 | it("array of double-nested objects", () => {
62 | const out = removePropertiesItems([
63 | { items: { properties: expected } },
64 | { items: { properties: expected } },
65 | { items: { properties: expected } },
66 | ]);
67 | expect(out).toEqual([expected, expected, expected]);
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/__tests__/lib/embed-docs/getUrlText.test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | isTextWithSubstance,
3 | splitTextByHeaders,
4 | } from "../../../lib/embed-docs/utils";
5 |
6 | describe("markdownToObject", () => {
7 | it("Simple", () => {
8 | let markdownText = `
9 | # Heading 1
10 | This is some text below heading 1.
11 | ## Heading 2
12 | This is some text below heading 2.
13 | This is more text below heading 2.
14 | ## Heading 2.1
15 | This is some text below heading 2.1.
16 | # Heading 3
17 | This is some text below heading 3.
18 | `;
19 | const out = splitTextByHeaders(markdownText);
20 | expect(out).toEqual({
21 | "Heading 1": "This is some text below heading 1.",
22 | "Heading 2":
23 | "This is some text below heading 2.\nThis is more text below heading 2.",
24 | "Heading 2.1": "This is some text below heading 2.1.",
25 | "Heading 3": "This is some text below heading 3.",
26 | });
27 | });
28 | it("No text below top heading", () => {
29 | let markdownText = `
30 | # Heading 1
31 |
32 | ## Heading 2
33 | This is some text below heading 2.
34 | This is more text below heading 2.
35 | ## Heading 2.1
36 | This is some text below heading 2.1.
37 | # Heading 3
38 | This is some text below heading 3.
39 | `;
40 | const out = splitTextByHeaders(markdownText);
41 | expect(out).toEqual({
42 | "Heading 1": "",
43 | "Heading 2":
44 | "This is some text below heading 2.\nThis is more text below heading 2.",
45 | "Heading 2.1": "This is some text below heading 2.1.",
46 | "Heading 3": "This is some text below heading 3.",
47 | });
48 | });
49 | it("Not starting with a heading", () => {
50 | let markdownText = `
51 | This is some text at the top.
52 | ## Heading 2
53 | This is some text below heading 2.
54 | This is more text below heading 2.
55 | ## Heading 2.1
56 | This is some text below heading 2.1.
57 | # Heading 3
58 | This is some text below heading 3.
59 | `;
60 | const out = splitTextByHeaders(markdownText);
61 | expect(out).toEqual({
62 | "": "This is some text at the top.",
63 | "Heading 2":
64 | "This is some text below heading 2.\nThis is more text below heading 2.",
65 | "Heading 2.1": "This is some text below heading 2.1.",
66 | "Heading 3": "This is some text below heading 3.",
67 | });
68 | });
69 | it("Includes a heading with newline before text", () => {
70 | let markdownText = `
71 | This is some text at the top.
72 | ## Heading 2
73 | This is some text below heading 2.
74 | This is more text below heading 2.
75 | ##
76 | Heading 2.1
77 | This is some text below heading 2.1.
78 | # Heading 3
79 | This is some text below heading 3.
80 | `;
81 | const out = splitTextByHeaders(markdownText);
82 | expect(out).toEqual({
83 | "": "This is some text at the top.",
84 | "Heading 2":
85 | "This is some text below heading 2.\nThis is more text below heading 2.",
86 | "Heading 2.1": "This is some text below heading 2.1.",
87 | "Heading 3": "This is some text below heading 3.",
88 | });
89 | });
90 | });
91 |
92 | describe("isTextWithSubstance", () => {
93 | it("Simple", () => {
94 | expect(isTextWithSubstance("")).toEqual(false);
95 | });
96 | it("", () => {
97 | const text =
98 | "* Invítanos con el correo [integrations@woffu.com](mailto:integrations@woffu.com) a tu cuenta de a3innuva Nómina para vincularla con tu cuenta de Woffu.";
99 | expect(isTextWithSubstance(text)).toEqual(true);
100 | });
101 | });
102 |
--------------------------------------------------------------------------------
/__tests__/lib/parsers/parseDataAnalysisResponse.test.ts:
--------------------------------------------------------------------------------
1 | import { parseDataAnalysisResponse } from "../../../lib/parsers/dataAnalysis";
2 |
3 | describe("parseDataAnalysisResponse", () => {
4 | it("basic", () => {
5 | const code = parseDataAnalysisResponse(`Thoughts: I am. I think. I will.
6 |
7 | Code:
8 | \`\`\`
9 | var graphData = {
10 | graphTitle: "title",
11 | type: "line",
12 | data: [
13 | { x: 1, y: 2 },
14 | { x: 2, y: 3 },
15 | ],
16 | xLabel: "x",
17 | yLabel: "y",
18 | };
19 | \`\`\``);
20 | expect(code).toEqual({
21 | code: `var graphData = {
22 | graphTitle: "title",
23 | type: "line",
24 | data: [
25 | { x: 1, y: 2 },
26 | { x: 2, y: 3 },
27 | ],
28 | xLabel: "x",
29 | yLabel: "y",
30 | };`,
31 | });
32 | });
33 | it("all options for end of ``` line", () => {
34 | const options = ["js", "javascript", "ts", "typescript"];
35 | for (const option of options) {
36 | const code = parseDataAnalysisResponse(`Thoughts: I am. I think. I will.
37 |
38 | Code:
39 | \`\`\`${option}
40 | var graphData = {
41 | graphTitle: "title",
42 | type: "line",
43 | data: [
44 | { x: 1, y: 2 },
45 | { x: 2, y: 3 },
46 | ],
47 | xLabel: "x",
48 | yLabel: "y",
49 | };
50 | \`\`\``);
51 | expect(code).toEqual({
52 | code: `var graphData = {
53 | graphTitle: "title",
54 | type: "line",
55 | data: [
56 | { x: 1, y: 2 },
57 | { x: 2, y: 3 },
58 | ],
59 | xLabel: "x",
60 | yLabel: "y",
61 | };`,
62 | });
63 | }
64 | });
65 | it("no code", () => {
66 | const code = parseDataAnalysisResponse(`Thoughts: I am. I think. I will.`);
67 | expect(code).toEqual(null);
68 | });
69 | it("includes fetch()", () => {
70 | const code = parseDataAnalysisResponse(`Thoughts: I am. I think. I will.
71 |
72 | Code:
73 | \`\`\`
74 | fetch("https://some-url.com")
75 | \`\`\``);
76 | expect(code).toEqual(null);
77 | });
78 | it("graphData not defined", () => {
79 | const code = parseDataAnalysisResponse(`Thoughts: I am. I think. I will.
80 |
81 | Code:
82 | \`\`\`
83 | let nonIllegalCode = "123";
84 | \`\`\``);
85 | expect(code).toEqual(null);
86 | });
87 | it("functionOutput redefined", () => {
88 | const code = parseDataAnalysisResponse(
89 | `Thoughts: I am. I think. I will.
90 |
91 | Code:
92 | \`\`\`
93 | let graphData = "123";
94 | let functionOutput = "456";
95 | \`\`\``,
96 | ["functionOutput"],
97 | );
98 | expect(code).toEqual(null);
99 | });
100 | it("Code throws error", () => {
101 | const code = parseDataAnalysisResponse(`Thoughts: I am. I think. I will.
102 |
103 | Code:
104 | \`\`\`
105 | throw new Error("This is an error");
106 | \`\`\``);
107 | expect(code).toEqual({ error: "This is an error" });
108 | });
109 | });
110 |
--------------------------------------------------------------------------------
/__tests__/lib/v2/edge-runtime/ai.test.ts:
--------------------------------------------------------------------------------
1 | import { hideLongGraphOutputs } from "../../../../lib/v2/edge-runtime/ai";
2 |
3 | describe("hideLongGraphOutputs", () => {
4 | it("basic", () => {
5 | const out = hideLongGraphOutputs(
6 | [
7 | {
8 | role: "function",
9 | name: "plot",
10 | content: JSON.stringify({
11 | type: "bar",
12 | data: new Array(250).fill({
13 | x: "Attack helicopter",
14 | y: 345769134950.346088724,
15 | badgers:
16 | "are sometimes culled in the country because they have bovine TB (colloquially known as badger pox)",
17 | }),
18 | }),
19 | },
20 | ],
21 | ["plot"],
22 | );
23 | expect(out.chatGptPrompt).toEqual([
24 | {
25 | role: "function",
26 | name: "plot",
27 | content:
28 | JSON.stringify({
29 | type: "bar",
30 | data: new Array(3).fill({
31 | x: "Attack helicopter",
32 | y: 345769134950.346088724,
33 | badgers:
34 | "are sometimes culled in the country because they have bovine TB (colloquially known as badger pox)",
35 | }),
36 | }).slice(0, -2) +
37 | ",]}",
38 | },
39 | ]);
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/__tests__/lib/v2/edge-runtime/filter-actions.test.ts:
--------------------------------------------------------------------------------
1 | import { combineSelectedFunctions } from "../../../../lib/v2/edge-runtime/filterActions";
2 |
3 | describe("combineSelectedFunctions", () => {
4 | it("Picks the thoughts from the most selected functions", () => {
5 | expect(
6 | combineSelectedFunctions(
7 | [
8 | { selectedFunctions: ["a", "b"], thoughts: "" },
9 | { selectedFunctions: ["b", "c"], thoughts: "" },
10 | {
11 | selectedFunctions: ["a", "b", "c"],
12 | thoughts: "This is the one to select",
13 | },
14 | ],
15 | // @ts-ignore
16 | [{ name: "a" }, { name: "b" }, { name: "c" }],
17 | ),
18 | ).toStrictEqual({
19 | thoughts: ["This is the one to select", "", ""],
20 | actions: [{ name: "a" }, { name: "b" }, { name: "c" }],
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/__tests__/lib/v2/prompts/routing.test.ts:
--------------------------------------------------------------------------------
1 | import { parseRoutingOutput } from "../../../../lib/v2/prompts/routing";
2 |
3 | describe("parseRoutingOutput", () => {
4 | it("should return null if the output does not match the expected format", () => {
5 | expect(parseRoutingOutput("", true)).toBeNull();
6 | });
7 | it("should return the thoughts and choice if the output matches the expected format", () => {
8 | expect(
9 | parseRoutingOutput(
10 | "Thoughts:\n1. Think step-by-step how to answer the user's request\n2. break down the user's request into steps\n3. specifically name EVERY SINGLE function and variable you will use\n4. consider if the user's request requires searching by name - use DIRECT if so\n5. compare the user's request with CRITERIA\n6. state your choice\n\nChoice: DIRECT\n",
11 | true,
12 | ),
13 | ).toEqual({
14 | thoughts:
15 | "1. Think step-by-step how to answer the user's request\n2. break down the user's request into steps\n3. specifically name EVERY SINGLE function and variable you will use\n4. consider if the user's request requires searching by name - use DIRECT if so\n5. compare the user's request with CRITERIA\n6. state your choice",
16 | choice: "DIRECT",
17 | });
18 | });
19 | it("not finished streaming", () => {
20 | expect(
21 | parseRoutingOutput(
22 | "Thoughts:\n1. Think step-by-step how to answer the user's request\n2. break down the user's request into steps\n3. specifically name EVERY SINGLE function and variable you will use\n4. consider if the user's request requires searching by name - use DIRECT if so\n5. compare the user's request with CRITERIA\n6. state your choice\n\nChoice: DIREC",
23 | true,
24 | ),
25 | ).toBeNull();
26 | });
27 | it("not valid answer, but we move", () => {
28 | expect(
29 | parseRoutingOutput(
30 | "Thoughts:\n1. Think step-by-step how to answer the user's request\n2. break down the user's request into steps\n3. specifically name EVERY SINGLE function and variable you will use\n4. consider if the user's request requires searching by name - use DIRECT if so\n5. compare the user's request with CRITERIA\n6. state your choice\n\nChoice: this ",
31 | true,
32 | ),
33 | ).toEqual({
34 | thoughts:
35 | "1. Think step-by-step how to answer the user's request\n2. break down the user's request into steps\n3. specifically name EVERY SINGLE function and variable you will use\n4. consider if the user's request requires searching by name - use DIRECT if so\n5. compare the user's request with CRITERIA\n6. state your choice",
36 | choice: "this",
37 | });
38 | });
39 | it("streaming ended", () => {
40 | expect(
41 | parseRoutingOutput(
42 | "Thoughts:\n1. Think step-by-step how to answer the user's request\n2. break down the user's request into steps\n3. specifically name EVERY SINGLE function and variable you will use\n4. consider if the user's request requires searching by name - use DIRECT if so\n5. compare the user's request with CRITERIA\n6. state your choice\n\nChoice: CODE",
43 | false,
44 | ),
45 | ).toEqual({
46 | thoughts:
47 | "1. Think step-by-step how to answer the user's request\n2. break down the user's request into steps\n3. specifically name EVERY SINGLE function and variable you will use\n4. consider if the user's request requires searching by name - use DIRECT if so\n5. compare the user's request with CRITERIA\n6. state your choice",
48 | choice: "CODE",
49 | });
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/__tests__/lib/v2/prompts/summariseChatHistory.test.ts:
--------------------------------------------------------------------------------
1 | import { getChatHistoryText } from "../../../../lib/v2/prompts/summariseChatHistory";
2 |
3 | describe("summariseChatHistory", () => {
4 | it("", () => {
5 | const historyText = getChatHistoryText([
6 | {
7 | role: "user",
8 | content: "I want to book a flight to Paris",
9 | },
10 | {
11 | role: "assistant",
12 | content: "Sure, when would you like to go?",
13 | },
14 | {
15 | role: "user",
16 | content: "Next week",
17 | },
18 | {
19 | role: "function",
20 | name: "getChatHistoryText",
21 | content: "I'm sorry, I don't understand",
22 | },
23 | ]);
24 | expect(historyText).toEqual({
25 | pastConversation:
26 | "User (oldest): I want to book a flight to Paris\n\nAssistant (most recent): Sure, when would you like to go?",
27 | numPastMessagesIncluded: 3,
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/__tests__/lib/v2/prompts/utils.test.ts:
--------------------------------------------------------------------------------
1 | import { parseTellUser } from "../../../../lib/v2/prompts/utils";
2 |
3 | describe("parseTellUser", () => {
4 | it("empty", () => {
5 | expect(parseTellUser("")).toEqual("");
6 | });
7 | it("no keyword", () => {
8 | expect(parseTellUser("hello")).toEqual("hello");
9 | });
10 | it("no keyword, multiple lines", () => {
11 | expect(parseTellUser("hello\nworld")).toEqual("hello\nworld");
12 | });
13 | it("Just tell user", () => {
14 | expect(parseTellUser("Tell user: hello")).toEqual("hello");
15 | });
16 | it("Other section, no tell user", () => {
17 | expect(parseTellUser("Thoughts: hello")).toEqual("Thoughts: hello");
18 | });
19 | it("Other section, tell user", () => {
20 | expect(parseTellUser("Thoughts: hello\nTell user: world")).toEqual("world");
21 | });
22 | it("Other section, tell user, multiple lines", () => {
23 | expect(parseTellUser("Thoughts: hello\nTell user: world\nworld")).toEqual(
24 | "world\nworld",
25 | );
26 | });
27 | it("Wacky real world example", () => {
28 | const input = `Tell user:
29 | Ava interacted with multiple companies this week. Here is a list of those companies:
30 |
31 | 1. Company A
32 | 2. Company B
33 | 3. Company C
34 | 4. Company D
35 | 5. Company E
36 | 6. Company F
37 | 7. Company G
38 | 8. Company H
39 |
40 | Reasoning:
41 | 1. The coder used the search_engagements function with the specified parameters to retrieve Ava's engagements this week.
42 | 2. For each engagement, they then used the search_contacts function to get the associated contact's information.
43 | 3. However, based on the logs, it appears that there was no data returned for the companies Ava interacted with.
44 | 4. This could be due to a lack of data or an issue with retrieving company information from contact IDs.
45 |
46 | Please note that without specific company names in our database, I am unable to provide you with more detailed information about these interactions at this time.
47 |
48 | If you have any other questions or need further assistance, please let me know!`;
49 | expect(parseTellUser(input)).toEqual(
50 | `Ava interacted with multiple companies this week. Here is a list of those companies:
51 |
52 | 1. Company A
53 | 2. Company B
54 | 3. Company C
55 | 4. Company D
56 | 5. Company E
57 | 6. Company F
58 | 7. Company G
59 | 8. Company H`,
60 | );
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/__tests__/lib/v3/utils.test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | convertIsoToHumanReadable,
3 | fillVariables,
4 | } from "../../../lib/v3/utils";
5 |
6 | describe("convertIsoToHumanReadable", () => {
7 | it("basic", () => {
8 | expect(convertIsoToHumanReadable("2024-01-01")).toEqual("1st January 2024");
9 | expect(convertIsoToHumanReadable("2024-05-30")).toEqual("30th May 2024");
10 | });
11 | });
12 |
13 | describe("fillVariables", () => {
14 | const noVariableObjs = [
15 | {
16 | text: "This is some text without variables",
17 | embedded_text: "This is some text without variables",
18 | variable_values: {},
19 | primary_question: true,
20 | },
21 | ];
22 | it("no variables", () => {
23 | expect(fillVariables(noVariableObjs, {}, {})).toEqual(noVariableObjs);
24 | });
25 | const variableObj = [
26 | {
27 | text: "This is some text with a variable: {variable1}",
28 | embedded_text: "This is some text with a variable: {variable1}",
29 | variable_values: {},
30 | primary_question: true,
31 | },
32 | ];
33 | it("text with 1 variable", () => {
34 | expect(fillVariables(variableObj, { variable1: "value1" }, {})).toEqual([
35 | {
36 | text: "This is some text with a variable: {variable1}",
37 | embedded_text: "This is some text with a variable: value1",
38 | variable_values: { variable1: "value1" },
39 | primary_question: true,
40 | },
41 | ]);
42 | });
43 | it("text with 1 variable: embed all", () => {
44 | expect(
45 | fillVariables(variableObj, {}, { variable1: ["value1", "value2"] }),
46 | ).toEqual([
47 | {
48 | text: "This is some text with a variable: {variable1}",
49 | embedded_text: "This is some text with a variable: value1",
50 | variable_values: { variable1: "value1" },
51 | primary_question: true,
52 | },
53 | {
54 | text: "This is some text with a variable: {variable1}",
55 | embedded_text: "This is some text with a variable: value2",
56 | variable_values: { variable1: "value2" },
57 | primary_question: true,
58 | },
59 | ]);
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/__tests__/prompts/apiMockPrompt.test.ts:
--------------------------------------------------------------------------------
1 | import apiMockPrompt from "../../lib/prompts/apiMock";
2 | import { Properties } from "../../lib/models";
3 |
4 | describe("apiMockPrompt", () => {
5 | it("Test object deconstruction works correctly", () => {
6 | const responseType: Properties = {
7 | user: {
8 | type: "string",
9 | description: "The user's name",
10 | path: ["user"],
11 | },
12 | };
13 |
14 | const res = apiMockPrompt(
15 | "/very/nice/path",
16 | "GET",
17 | null,
18 | responseType,
19 | undefined,
20 | false,
21 | );
22 |
23 | const extractedString = res[1].content
24 | .split("Fields\n---")[3]
25 | .split("Response\n---")[0]
26 | .trim();
27 |
28 | expect(extractedString).toEqual(`user (string): The user's name`);
29 | });
30 |
31 | it("Test object deconstruction works correctly nested property", () => {
32 | const responseType: Properties = {
33 | "user.id": {
34 | type: "string",
35 | description: "The user's id",
36 | path: ["user, id"],
37 | },
38 | };
39 |
40 | const res = apiMockPrompt(
41 | "/very/nice/path",
42 | "GET",
43 | null,
44 | responseType,
45 | undefined,
46 | false,
47 | );
48 |
49 | const extractedString = res[1].content
50 | .split("Fields\n---")[3]
51 | .split("Response\n---")[0]
52 | .trim();
53 |
54 | expect(extractedString).toEqual(`user.id (string): The user's id`);
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/__tests__/prompts/suggestFollowUps.test.ts:
--------------------------------------------------------------------------------
1 | import { filterConversationForFollowUps } from "../../lib/prompts/suggestFollowUps";
2 |
3 | describe("filterConversationForFollowUps", () => {
4 | it("Simple", () => {
5 | const out = filterConversationForFollowUps([
6 | {
7 | role: "user",
8 | content: "Hello",
9 | },
10 | ]);
11 | expect(out).toEqual([
12 | {
13 | role: "assistant",
14 | content: "Hello",
15 | },
16 | ]);
17 | });
18 | it("Simple 2", () => {
19 | const out = filterConversationForFollowUps([
20 | {
21 | role: "user",
22 | content: "Hello",
23 | },
24 | {
25 | role: "assistant",
26 | content: "Hi",
27 | },
28 | ]);
29 | expect(out).toEqual([
30 | {
31 | role: "assistant",
32 | content: "Hello",
33 | },
34 | {
35 | role: "user",
36 | content: "Hi",
37 | },
38 | ]);
39 | });
40 | it("Actually requires filtering", () => {
41 | const out = filterConversationForFollowUps([
42 | {
43 | role: "user",
44 | content: "Hello",
45 | },
46 | {
47 | role: "assistant",
48 | content: "Hi",
49 | },
50 | {
51 | role: "function",
52 | name: "summarizeText",
53 | content: "This is a summary",
54 | },
55 | {
56 | role: "assistant",
57 | content: "I returned a summary",
58 | },
59 | ]);
60 | expect(out).toEqual([
61 | {
62 | role: "assistant",
63 | content: "Hello",
64 | },
65 | {
66 | role: "user",
67 | content: "I returned a summary",
68 | },
69 | ]);
70 | });
71 | it("Requires filtering 2", () => {
72 | const out = filterConversationForFollowUps([
73 | {
74 | role: "user",
75 | content: "Hello",
76 | },
77 | {
78 | role: "assistant",
79 | content: "Hi",
80 | },
81 | {
82 | role: "function",
83 | name: "summarizeText",
84 | content: "This is a summary",
85 | },
86 | {
87 | role: "assistant",
88 | content: "I returned a summary",
89 | },
90 | {
91 | role: "user",
92 | content: "Thanks for that",
93 | },
94 | {
95 | role: "assistant",
96 | content: "What is going on???",
97 | },
98 | {
99 | role: "function",
100 | name: "summarizeText",
101 | content: "This shouldn't be shown!!!",
102 | },
103 | {
104 | role: "assistant",
105 | content: "You're welcome",
106 | },
107 | ]);
108 | expect(out).toEqual([
109 | {
110 | role: "assistant",
111 | content: "Hello",
112 | },
113 | {
114 | role: "user",
115 | content: "I returned a summary",
116 | },
117 | {
118 | role: "assistant",
119 | content: "Thanks for that",
120 | },
121 | {
122 | role: "user",
123 | content: "You're welcome",
124 | },
125 | ]);
126 | });
127 | });
128 |
--------------------------------------------------------------------------------
/__tests__/prompts/summarizeText.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from "@jest/globals";
2 | import { getSummarizeTextPrompt } from "../../lib/prompts/summarizeText";
3 |
4 | describe("getSummarizeTextPrompt", () => {
5 | it("basic prompt", () => {
6 | // @ts-ignore
7 | const summarizePrompt = getSummarizeTextPrompt("This is a test", {
8 | name: "test",
9 | description: "test description",
10 | });
11 | expect(summarizePrompt).toEqual([
12 | {
13 | role: "system",
14 | content: `Your task is to summarize a document using bullet points and short simple sentences.
15 |
16 | For context of what to include in your summary, you are working for test. test description
17 |
18 | Your response should be a maximum of 6 bullet points. Only use three, four or five bullet points if you can. Only write short simple sentences. Only write one sentence per bullet point. Be as succinct as possible.
19 |
20 | Include ALL numbers and statistics. THIS IS VERY IMPORTANT! DO NOT FORGET THIS! YOU SHOULD BE PARANOID THAT YOU MIGHT FORGET THIS!
21 |
22 | DO NOT include legal disclaimers, privacy policies or copyright information. DO NOT FORGET THIS!
23 |
24 | Your summary should follow this format:
25 | - Bullet 1
26 | - Bullet 2
27 |
28 | The user's message will be the text to summarize.
29 |
30 | Total word limit of the summary: 200 words`,
31 | },
32 | {
33 | role: "user",
34 | content: "This is a test",
35 | },
36 | ]);
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/__tests__/swagger-to-actions.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "@jest/globals";
2 | import {
3 | operationIdToFunctionName,
4 | replaceMarkdownLinks,
5 | requestToFunctionName,
6 | } from "../pages/api/swagger-to-actions";
7 |
8 | describe("request to function name", () => {
9 | it("list organizations", () => {
10 | const fnName = requestToFunctionName("get", {}, "/api/v1/organizations");
11 | expect(fnName).toEqual("list_organizations");
12 | });
13 | it("get organization by id", () => {
14 | const fnName = requestToFunctionName(
15 | "get",
16 | { parameters: [{ in: "path", name: "id" }] },
17 | "/api/v1/organizations/{id}",
18 | );
19 | expect(fnName).toEqual("get_organization_by_id");
20 | });
21 | it("create chat completion", () => {
22 | const fnName = requestToFunctionName("post", {}, "/chat/completions");
23 | expect(fnName).toEqual("create_chat_completion");
24 | });
25 | it("get by parent_lookup_organization_id domain", () => {
26 | const fnName = requestToFunctionName(
27 | "get",
28 | { parameters: [{ in: "path", name: "parent_lookup_organization_id" }] },
29 | "/api/organizations/{parent_lookup_organization_id}/domains/",
30 | );
31 | expect(fnName).toEqual("get_by_parent_lookup_organization_id_domain");
32 | });
33 | it("get_plugin_activity", () => {
34 | const fnName = requestToFunctionName(
35 | "get",
36 | { parameters: [{ in: "path", name: "parent_lookup_organization_id" }] },
37 | "/api/organizations/{parent_lookup_organization_id}/plugins/activity/",
38 | );
39 | expect(fnName).toEqual("get_plugin_activity");
40 | });
41 | });
42 |
43 | describe("replaceMarkdownLinks", () => {
44 | it("undefined passes through", () => {
45 | let result = replaceMarkdownLinks(undefined);
46 | expect(result).toEqual(undefined);
47 | });
48 | it("replace 1 markdown link", () => {
49 | let testString = "there's [something](https://something.com)";
50 | let result = replaceMarkdownLinks(testString);
51 | expect(result).toEqual("there's something");
52 | });
53 | it("replace 2 markdown links", () => {
54 | let testString =
55 | "there's [something](https://something.com) [here](https://something.com)";
56 | let result = replaceMarkdownLinks(testString);
57 | expect(result).toEqual("there's something here");
58 | });
59 | });
60 |
61 | describe("operationIdToFunctionName", () => {
62 | it("handles simple camel case", () => {
63 | expect(operationIdToFunctionName("camelCase")).toBe("camel_case");
64 | });
65 |
66 | it("handles multiple capital letters in a row", () => {
67 | expect(operationIdToFunctionName("camelCaseJSON")).toBe("camel_case_json");
68 | });
69 |
70 | it("handles words without capital letters", () => {
71 | expect(operationIdToFunctionName("camelcase")).toBe("camelcase");
72 | });
73 |
74 | it("handles single words", () => {
75 | expect(operationIdToFunctionName("Camel")).toBe("camel");
76 | });
77 |
78 | it("handles words with numbers", () => {
79 | expect(operationIdToFunctionName("camelCase2Go")).toBe("camel_case2_go");
80 | });
81 | });
82 |
--------------------------------------------------------------------------------
/__tests__/testActions.ts:
--------------------------------------------------------------------------------
1 | import { Action } from "../lib/types";
2 |
3 | export const testActions: Action[] = [
4 | {
5 | tag: 1,
6 | action_type: "http",
7 | description: "",
8 | active: true,
9 | created_at: "2022-01-01T00:00:00Z",
10 | id: 1,
11 | keys_to_keep: null,
12 | name: "action_a",
13 | org_id: 1,
14 | parameters: null,
15 | path: "/api/v1/endpoint1",
16 | request_body_contents: null,
17 | request_method: "POST",
18 | responses: null,
19 | api_id: "12345",
20 | requires_confirmation: false,
21 | link_url: "",
22 | link_name: "",
23 | },
24 | {
25 | tag: 1,
26 | action_type: "http",
27 | description: "",
28 | active: true,
29 | created_at: "2022-01-01T00:00:00Z",
30 | id: 2,
31 | keys_to_keep: null,
32 | name: "action_b",
33 | org_id: 1,
34 | parameters: null,
35 | path: "/api/v2/endpoint2",
36 | request_body_contents: null,
37 | request_method: "GET",
38 | responses: null,
39 | api_id: "12345",
40 | requires_confirmation: false,
41 | link_url: "",
42 | link_name: "",
43 | },
44 | {
45 | tag: 1,
46 | action_type: "http",
47 | description: "",
48 | active: true,
49 | created_at: "2022-01-01T00:00:00Z",
50 | id: 3,
51 | keys_to_keep: null,
52 | name: "action_c",
53 | org_id: 1,
54 | parameters: null,
55 | path: "/api/v2/endpoint3",
56 | request_body_contents: null,
57 | request_method: "PUT",
58 | responses: null,
59 | api_id: "12345",
60 | requires_confirmation: false,
61 | link_url: "",
62 | link_name: "",
63 | },
64 | {
65 | tag: 1,
66 | action_type: "http",
67 | description: "",
68 | active: true,
69 | created_at: "2022-01-01T00:00:00Z",
70 | id: 3,
71 | keys_to_keep: null,
72 | name: "action_d",
73 | org_id: 1,
74 | parameters: null,
75 | path: "/api/v2/endpoint3/{id}",
76 | request_body_contents: null,
77 | request_method: "PUT",
78 | responses: null,
79 | api_id: "12345",
80 | requires_confirmation: false,
81 | link_url: "",
82 | link_name: "",
83 | },
84 | {
85 | tag: 1,
86 | action_type: "http",
87 | description: "",
88 | active: true,
89 | created_at: "2022-01-01T00:00:00Z",
90 | id: 3,
91 | keys_to_keep: null,
92 | name: "action_e",
93 | org_id: 1,
94 | parameters: null,
95 | path: "/api/v2/{id}/endpoint3",
96 | request_body_contents: null,
97 | request_method: "PUT",
98 | responses: null,
99 | api_id: "12345",
100 | requires_confirmation: false,
101 | link_url: "",
102 | link_name: "",
103 | },
104 | {
105 | tag: 1,
106 | action_type: "http",
107 | description: "",
108 | active: true,
109 | created_at: "2022-01-01T00:00:00Z",
110 | id: 3,
111 | keys_to_keep: null,
112 | name: "action_f",
113 | org_id: 1,
114 | parameters: null,
115 | path: "/api/0/teams/{organization_slug}/{team_slug}/",
116 | request_body_contents: null,
117 | request_method: "PUT",
118 | responses: null,
119 | api_id: "12345",
120 | requires_confirmation: false,
121 | link_url: "",
122 | link_name: "",
123 | },
124 | ];
125 |
--------------------------------------------------------------------------------
/__tests__/testData/Henry-Pulver-CV.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Superflows-AI/superflows/4c8c40c05fa7ba812f1a0c78d00be61ca932b93c/__tests__/testData/Henry-Pulver-CV.pdf
--------------------------------------------------------------------------------
/__tests__/testData/LOA_superflows-sign-in_413.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Superflows-AI/superflows/4c8c40c05fa7ba812f1a0c78d00be61ca932b93c/__tests__/testData/LOA_superflows-sign-in_413.pdf
--------------------------------------------------------------------------------
/__tests__/utils/isDate.test.ts:
--------------------------------------------------------------------------------
1 | import { isDate } from "../../lib/utils";
2 |
3 | describe("isDate", () => {
4 | it("date ISO with Z format", () => {
5 | expect(isDate("2023-04-13T21:21:00Z")).toBeTruthy();
6 | });
7 | it("date ISO with Z format and milliseconds", () => {
8 | expect(isDate("2023-04-13T21:21:00.475Z")).toBeTruthy();
9 | });
10 | it("date ISO with milliseconds", () => {
11 | expect(isDate("2023-04-13T21:21:00.475")).toBeTruthy();
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/__tests__/utils/isURL.test.ts:
--------------------------------------------------------------------------------
1 | import { isUrl } from "../../lib/utils";
2 |
3 | const validUrls = [
4 | "x.com",
5 | "www.google.com",
6 | "https://www.google.com",
7 | "http://www.google.com",
8 | "https://google.com",
9 | "https://google.com/search",
10 | "https://google.com/search?q=hello",
11 | "https://google.com/search?q=hello&user=me",
12 | "https://google.com/search/?q=hello&user=me",
13 | "https://google.com/search/#hello",
14 | "https://google.com/search/hello.html",
15 | "https://google.com/search/hello.html?user=me",
16 | "https://google.com/search/hello.html?user=me#hello",
17 | ];
18 |
19 | const validUrlsWithIDX = [
20 | "x.com/ID1",
21 | "www.google.com/ID7",
22 | "https://www.google.com?id=ID22",
23 | ];
24 |
25 | const invalidUrls = [
26 | "henry@gmail.com",
27 | "183bd6ff-e8fd-44a6-a3a8-eed9cb1082df",
28 | "This isn't a URL",
29 | "James Rowland",
30 | "alphanumericStringNoNumbers",
31 | "alphanumeric1With2Numbers",
32 | ];
33 |
34 | describe("isURL", () => {
35 | it("should return true for valid URLs", () => {
36 | for (const url of validUrls) {
37 | expect(isUrl(url)).toBe(true);
38 | }
39 | });
40 | it("should return false for invalid URLs", () => {
41 | for (const url of invalidUrls) {
42 | expect(isUrl(url)).toBe(false);
43 | }
44 | });
45 | it("Not a URL if it has an IDX in it", () => {
46 | for (const url of validUrlsWithIDX) {
47 | expect(isUrl(url)).toBe(false);
48 | }
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/__tests__/utils/joinArraysNoDuplicates.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "@jest/globals";
2 | import { joinArraysNoDuplicates } from "../../lib/utils";
3 |
4 | describe("Tests for joinArrayWithoutDuplication function", () => {
5 | it("Checks if function returns joined arrays without duplications", () => {
6 | const firstArray = [
7 | { id: 1, name: "John" },
8 | { id: 2, name: "Paul" },
9 | ];
10 | const secondArray = [
11 | { id: 3, name: "George" },
12 | { id: 4, name: "Ringo" },
13 | ];
14 | const expected = [
15 | { id: 1, name: "John" },
16 | { id: 2, name: "Paul" },
17 | { id: 3, name: "George" },
18 | { id: 4, name: "Ringo" },
19 | ];
20 |
21 | expect(joinArraysNoDuplicates(firstArray, secondArray, "id")).toEqual(
22 | expected,
23 | );
24 | });
25 | it("Checks if function returns joined arrays with duplications", () => {
26 | const firstArray = [
27 | { id: 1, name: "John" },
28 | { id: 2, name: "Paul" },
29 | { id: 3, name: "George" },
30 | ];
31 | const secondArray = [
32 | { id: 2, name: "Paul" },
33 | { id: 3, name: "George" },
34 | { id: 4, name: "Ringo" },
35 | ];
36 | const expected = [
37 | { id: 1, name: "John" },
38 | { id: 2, name: "Paul" },
39 | { id: 3, name: "George" },
40 | { id: 4, name: "Ringo" },
41 | ];
42 |
43 | expect(joinArraysNoDuplicates(firstArray, secondArray, "id")).toEqual(
44 | expected,
45 | );
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/__tests__/utils/stripTrailingAndCurly.test.ts:
--------------------------------------------------------------------------------
1 | import { stripTrailingAndCurly } from "../../lib/utils";
2 | import { describe, expect, it } from "@jest/globals";
3 |
4 | describe("stripTrailingAndCurly", () => {
5 | it("should remove nothing", () => {
6 | expect(stripTrailingAndCurly("/api")).toBe("/api");
7 | });
8 |
9 | it("should remove trailing slashes", () => {
10 | expect(stripTrailingAndCurly("/api/")).toBe("/api");
11 | });
12 |
13 | it("should remove curly brackets", () => {
14 | expect(stripTrailingAndCurly("/api/{id}")).toBe("/api");
15 | });
16 |
17 | it("should remove trailing slashes and curly brackets", () => {
18 | expect(stripTrailingAndCurly("/api/{id}/")).toBe("/api");
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/components/actions/consts.ts:
--------------------------------------------------------------------------------
1 | export const exampleParameters = [
2 | {
3 | in: "query",
4 | name: "name",
5 | description: "Name of the company",
6 | required: true,
7 | schema: {
8 | type: "string",
9 | enum: ["Google", "Facebook", "Apple"],
10 | },
11 | },
12 | ];
13 |
14 | export const exampleRequestBody = {
15 | "application/json": {
16 | schema: {
17 | required: ["name"],
18 | type: "object",
19 | properties: {
20 | name: {
21 | type: "string",
22 | enum: ["Google", "Facebook", "Apple"],
23 | },
24 | },
25 | },
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/components/approval/question.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function QuestionText(props: { questionText: string }) {
4 | return (
5 |
6 | {props.questionText
7 | .split(/(\{\w+})/g)
8 | .map((part: string, idx: number) => {
9 | if (part.match(/\{(\w+)}/)) {
10 | return (
11 |
12 | {part}
13 |
14 | );
15 | } else {
16 | return {part};
17 | }
18 | })}
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/components/approval/types.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ApprovalAnswer,
3 | ApprovalAnswerMessage,
4 | ApprovalQuestion,
5 | } from "../../lib/types";
6 |
7 | export type UIAnswerType = Omit<
8 | ApprovalAnswer,
9 | "created_at" | "description" | "fnName" | "org_id" | "is_docs"
10 | > & {
11 | approval_questions: Pick[];
12 | };
13 |
14 | export type UIMessageData = Pick<
15 | ApprovalAnswerMessage,
16 | "id" | "raw_text" | "message_idx" | "message_type" | "generated_output"
17 | >;
18 |
--------------------------------------------------------------------------------
/components/autoGrowingTextarea.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from "react";
2 |
3 | export function AutoGrowingTextArea(props: {
4 | className: string;
5 | placeholder: string;
6 | value: string;
7 | onChange: (e: React.ChangeEvent) => void;
8 | onKeyDown?: (e: React.KeyboardEvent) => void;
9 | minHeight?: number;
10 | maxHeight?: number;
11 | onBlur?: React.FocusEventHandler;
12 | disabled?: boolean;
13 | ref?: React.RefObject;
14 | }) {
15 | const localRef = useRef(null);
16 | const ref = props.ref ?? localRef;
17 |
18 | useEffect(() => {
19 | if (ref.current === null) return;
20 | // @ts-ignore
21 | ref.current.style.height = "5px";
22 |
23 | let maxH = props.maxHeight ?? 500;
24 | let minH = props.minHeight ?? 0;
25 |
26 | // @ts-ignore
27 | ref.current.style.height =
28 | // @ts-ignore
29 | Math.max(Math.min(ref.current.scrollHeight, maxH), minH) + "px";
30 | }, [ref.current, props.value]);
31 |
32 | return (
33 |