diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76add87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/package.json b/package.json index 2ddb5ec..41c80fb 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,21 @@ { - "name": "@weaviate/agents-client", + "name": "@weaviate/agents", "version": "0.0.1", "description": "JS/TS client for Weaviate Agents", - "scripts": {}, + "exports": "./dist/index.js", + "type": "module", + "scripts": { + "build": "tsc" + }, "keywords": [ "weaviate" ], "author": "Weaviate", - "license": "SEE LICENSE IN LICENSE" + "license": "SEE LICENSE IN LICENSE", + "peerDependencies": { + "weaviate-client": "^3.5.2" + }, + "devDependencies": { + "typescript": "^5.8.3" + } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..a8ff365 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,393 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + weaviate-client: + specifier: ^3.5.2 + version: 3.5.2 + devDependencies: + typescript: + specifier: ^5.8.3 + version: 5.8.3 + +packages: + + '@graphql-typed-document-node/core@3.2.0': + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@grpc/grpc-js@1.13.3': + resolution: {integrity: sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@types/node@22.15.3': + resolution: {integrity: sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==} + + abort-controller-x@0.4.3: + resolution: {integrity: sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + graphql-request@6.1.0: + resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} + peerDependencies: + graphql: 14 - 16 + + graphql@16.11.0: + resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + nice-grpc-client-middleware-retry@3.1.11: + resolution: {integrity: sha512-xW/imz/kNG2g0DwTfH2eYEGrg1chSLrXtvGp9fg2qkhTgGFfAS/Pq3+t+9G8KThcC4hK/xlEyKvZWKk++33S6A==} + + nice-grpc-common@2.0.2: + resolution: {integrity: sha512-7RNWbls5kAL1QVUOXvBsv1uO0wPQK3lHv+cY1gwkTzirnG1Nop4cBJZubpgziNbaVc/bl9QJcyvsf/NQxa3rjQ==} + + nice-grpc@2.1.12: + resolution: {integrity: sha512-J1n4Wg+D3IhRhGQb+iqh2OpiM0GzTve/kf2lnlW4S+xczmIEd0aHUDV1OsJ5a3q8GSTqJf+s4Rgg1M8uJltarw==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + protobufjs@7.5.0: + resolution: {integrity: sha512-Z2E/kOY1QjoMlCytmexzYfDm/w5fKAiRwpSzGtdnXW1zC88Z2yXazHHrOtwCzn+7wSxyE8PYM4rvVcMphF9sOA==} + engines: {node: '>=12.0.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + ts-error@1.0.6: + resolution: {integrity: sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + weaviate-client@3.5.2: + resolution: {integrity: sha512-FpBdljOkVqSBUk7rYxEGdeQ0NmvV7p5skZ8Hc5MInThQSGlKLr+TltR/b8Ivtd8JkSErwVvmyriXfBK5ubNiXg==} + engines: {node: '>=18.0.0'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + +snapshots: + + '@graphql-typed-document-node/core@3.2.0(graphql@16.11.0)': + dependencies: + graphql: 16.11.0 + + '@grpc/grpc-js@1.13.3': + dependencies: + '@grpc/proto-loader': 0.7.15 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.7.15': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.0 + yargs: 17.7.2 + + '@js-sdsl/ordered-map@4.4.2': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@types/node@22.15.3': + dependencies: + undici-types: 6.21.0 + + abort-controller-x@0.4.3: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + emoji-regex@8.0.0: {} + + escalade@3.2.0: {} + + get-caller-file@2.0.5: {} + + graphql-request@6.1.0(graphql@16.11.0): + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.11.0) + cross-fetch: 3.2.0 + graphql: 16.11.0 + transitivePeerDependencies: + - encoding + + graphql@16.11.0: {} + + is-fullwidth-code-point@3.0.0: {} + + lodash.camelcase@4.3.0: {} + + long@5.3.2: {} + + nice-grpc-client-middleware-retry@3.1.11: + dependencies: + abort-controller-x: 0.4.3 + nice-grpc-common: 2.0.2 + + nice-grpc-common@2.0.2: + dependencies: + ts-error: 1.0.6 + + nice-grpc@2.1.12: + dependencies: + '@grpc/grpc-js': 1.13.3 + abort-controller-x: 0.4.3 + nice-grpc-common: 2.0.2 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + protobufjs@7.5.0: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 22.15.3 + long: 5.3.2 + + require-directory@2.1.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + tr46@0.0.3: {} + + ts-error@1.0.6: {} + + typescript@5.8.3: {} + + undici-types@6.21.0: {} + + uuid@9.0.1: {} + + weaviate-client@3.5.2: + dependencies: + abort-controller-x: 0.4.3 + graphql: 16.11.0 + graphql-request: 6.1.0(graphql@16.11.0) + long: 5.3.2 + nice-grpc: 2.1.12 + nice-grpc-client-middleware-retry: 3.1.11 + nice-grpc-common: 2.0.2 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..3fa76c2 --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export * from "./query/index.js"; diff --git a/src/query/agent.ts b/src/query/agent.ts new file mode 100644 index 0000000..ee04c2e --- /dev/null +++ b/src/query/agent.ts @@ -0,0 +1,61 @@ +import { WeaviateClient } from "weaviate-client"; +import { QueryAgentResponse } from "./response/response.js"; +import { mapResponse } from "./response/response-mapping.js"; + +export class QueryAgent { + private systemPrompt?: string; + private agentsHost: string; + + constructor( + private client: WeaviateClient, + private collections: string[], + { + systemPrompt, + agentsHost = "https://api.agents.weaviate.io", + }: QueryAgentOptions = {} + ) { + this.systemPrompt = systemPrompt; + this.agentsHost = agentsHost; + } + + async run( + query: string, + { viewProperties }: QueryAgentRunOptions = {} + ): Promise { + const { host, bearerToken, headers } = + await this.client.getConnectionDetails(); + + const response = await fetch(`${this.agentsHost}/agent/query`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: bearerToken!, + "X-Weaviate-Cluster-Url": host, + }, + body: JSON.stringify({ + headers, + query, + collection_names: this.collections, + collection_view_properties: viewProperties, + system_prompt: this.systemPrompt, + }), + }); + + const responseBody = await response.json(); + + if (!response.ok) { + throw Error(`Query agent failed. ${JSON.stringify(responseBody)}`); + } + + return mapResponse(responseBody); + } +} + +export type QueryAgentOptions = { + systemPrompt?: string; + agentsHost?: string; +}; + +export type QueryAgentRunOptions = { + viewProperties?: string[]; +}; diff --git a/src/query/index.ts b/src/query/index.ts new file mode 100644 index 0000000..88dcfbd --- /dev/null +++ b/src/query/index.ts @@ -0,0 +1,2 @@ +export * from "./agent.js"; +export * from "./response/index.js"; diff --git a/src/query/response/api-response.ts b/src/query/response/api-response.ts new file mode 100644 index 0000000..a8daee1 --- /dev/null +++ b/src/query/response/api-response.ts @@ -0,0 +1,94 @@ +import { + NumericMetrics, + TextMetrics, + BooleanMetrics, + ComparisonOperator, +} from "./response.js"; + +export type ApiQueryAgentResponse = { + original_query: string; + collection_names: string[]; + searches: ApiSearchResult[][]; + aggregations: ApiAggregationResult[][]; + usage: ApiUsage; + total_time: number; + aggregation_answer?: string; + has_aggregation_answer: boolean; + has_search_answer: boolean; + is_partial_answer: boolean; + missing_information: string[]; + final_answer: string; + sources: ApiSource[]; +}; + +export type ApiSearchResult = { + collection: string; + queries: string[]; + filters: ApiPropertyFilter[]; + filter_operators: "AND" | "OR"; +}; + +export type ApiPropertyFilter = + | ApiIntegerPropertyFilter + | ApiTextPropertyFilter + | ApiBooleanPropertyFilter; + +type ApiPropertyFilterBase = { + property_name: string; + operator: ComparisonOperator; +}; + +export type ApiIntegerPropertyFilter = ApiPropertyFilterBase & { + value: number; +}; + +export type ApiTextPropertyFilter = ApiPropertyFilterBase & { + value: string; +}; + +export type ApiBooleanPropertyFilter = ApiPropertyFilterBase & { + value: boolean; +}; + +export type ApiAggregationResult = { + collection: string; + search_query?: string; + groupby_property?: string; + aggregations: ApiPropertyAggregation[]; + filters: ApiPropertyFilter[]; +}; + +export type ApiPropertyAggregation = + | ApiIntegerPropertyAggregation + | ApiTextPropertyAggregation + | ApiBooleanPropertyAggregation; + +type ApiPropertyAggregationBase = { + property_name: string; +}; + +export type ApiIntegerPropertyAggregation = ApiPropertyAggregationBase & { + metrics: NumericMetrics; +}; + +export type ApiTextPropertyAggregation = ApiPropertyAggregationBase & { + metrics: TextMetrics; + top_occurrences_limit?: number; +}; + +export type ApiBooleanPropertyAggregation = ApiPropertyAggregationBase & { + metrics: BooleanMetrics; +}; + +export type ApiUsage = { + requests: number; + request_tokens?: number; + response_tokens?: number; + total_tokens?: number; + details?: Record; +}; + +export type ApiSource = { + object_id: string; + collection: string; +}; diff --git a/src/query/response/index.ts b/src/query/response/index.ts new file mode 100644 index 0000000..acec77d --- /dev/null +++ b/src/query/response/index.ts @@ -0,0 +1 @@ +export * from "./response.js"; diff --git a/src/query/response/response-mapping.ts b/src/query/response/response-mapping.ts new file mode 100644 index 0000000..98d68eb --- /dev/null +++ b/src/query/response/response-mapping.ts @@ -0,0 +1,104 @@ +import { + QueryAgentResponse, + SearchResult, + PropertyFilter, + AggregationResult, + PropertyAggregation, + Usage, + Source, +} from "./response.js"; + +import { + ApiQueryAgentResponse, + ApiSearchResult, + ApiPropertyFilter, + ApiAggregationResult, + ApiPropertyAggregation, + ApiUsage, + ApiSource, +} from "./api-response.js"; + +export const mapResponse = ( + response: ApiQueryAgentResponse +): QueryAgentResponse => { + const properties: ResponseProperties = { + originalQuery: response.original_query, + collectionNames: response.collection_names, + searches: mapSearches(response.searches), + aggregations: mapAggregations(response.aggregations), + usage: mapUsage(response.usage), + totalTime: response.total_time, + aggregationAnswer: response.aggregation_answer, + hasAggregationAnswer: response.has_aggregation_answer, + hasSearchAnswer: response.has_search_answer, + isPartialAnswer: response.is_partial_answer, + missingInformation: response.missing_information, + finalAnswer: response.final_answer, + sources: mapSources(response.sources), + }; + + return { + ...properties, + display: () => display(properties), + }; +}; + +const mapSearches = (searches: ApiSearchResult[][]): SearchResult[][] => + searches.map((searchGroup) => + searchGroup.map((result) => ({ + collection: result.collection, + queries: result.queries, + filters: result.filters.map(mapPropertyFilter), + filterOperators: result.filter_operators, + })) + ); + +const mapPropertyFilter = (filter: ApiPropertyFilter): PropertyFilter => ({ + propertyName: filter.property_name, + operator: filter.operator, + value: filter.value, +}); + +const mapAggregations = ( + aggregations: ApiAggregationResult[][] +): AggregationResult[][] => + aggregations.map((aggGroup) => + aggGroup.map((result) => ({ + collection: result.collection, + searchQuery: result.search_query, + groupbyProperty: result.groupby_property, + aggregations: result.aggregations.map(mapPropertyAggregation), + filters: result.filters.map(mapPropertyFilter), + })) + ); + +const mapPropertyAggregation = ( + aggregation: ApiPropertyAggregation +): PropertyAggregation => ({ + propertyName: aggregation.property_name, + metrics: aggregation.metrics, + topOccurrencesLimit: + "top_occurrences_limit" in aggregation + ? aggregation.top_occurrences_limit + : undefined, +}); + +const mapUsage = (usage: ApiUsage): Usage => ({ + requests: usage.requests, + requestTokens: usage.request_tokens, + responseTokens: usage.response_tokens, + totalTokens: usage.total_tokens, + details: usage.details, +}); + +const mapSources = (sources: ApiSource[]): Source[] => + sources.map((source) => ({ + objectId: source.object_id, + collection: source.collection, + })); + +const display = (response: ResponseProperties) => { + console.log(JSON.stringify(response, undefined, 2)); +}; + +type ResponseProperties = Omit; diff --git a/src/query/response/response.ts b/src/query/response/response.ts new file mode 100644 index 0000000..5780ebf --- /dev/null +++ b/src/query/response/response.ts @@ -0,0 +1,124 @@ +export type QueryAgentResponse = { + originalQuery: string; + collectionNames: string[]; + searches: SearchResult[][]; + aggregations: AggregationResult[][]; + usage: Usage; + totalTime: number; + aggregationAnswer?: string; + hasAggregationAnswer: boolean; + hasSearchAnswer: boolean; + isPartialAnswer: boolean; + missingInformation: string[]; + finalAnswer: string; + sources: Source[]; + display(): void; +}; + +export type SearchResult = { + collection: string; + queries: string[]; + filters: PropertyFilter[]; + filterOperators: "AND" | "OR"; +}; + +export type PropertyFilter = + | IntegerPropertyFilter + | TextPropertyFilter + | BooleanPropertyFilter; + +type PropertyFilterBase = { + propertyName: string; + operator: ComparisonOperator; +}; + +export type IntegerPropertyFilter = PropertyFilterBase & { + value: number; +}; + +export type TextPropertyFilter = PropertyFilterBase & { + value: string; +}; + +export type BooleanPropertyFilter = PropertyFilterBase & { + value: boolean; +}; + +export enum ComparisonOperator { + Equals = "=", + LessThan = "<", + GreaterThan = ">", + LessEqual = "<=", + GreaterEqual = ">=", + NotEquals = "!=", + Like = "LIKE", +} + +export type AggregationResult = { + collection: string; + searchQuery?: string; + groupbyProperty?: string; + aggregations: PropertyAggregation[]; + filters: PropertyFilter[]; +}; + +export type PropertyAggregation = + | IntegerPropertyAggregation + | TextPropertyAggregation + | BooleanPropertyAggregation; + +type PropertyAggregationBase = { + propertyName: string; +}; + +export type IntegerPropertyAggregation = PropertyAggregationBase & { + metrics: NumericMetrics; +}; + +export type TextPropertyAggregation = PropertyAggregationBase & { + metrics: TextMetrics; + topOccurrencesLimit?: number; +}; + +export type BooleanPropertyAggregation = PropertyAggregationBase & { + metrics: BooleanMetrics; +}; + +export enum NumericMetrics { + Count = "COUNT", + Maximum = "MAXIMUM", + Mean = "MEAN", + Median = "MEDIAN", + Minimum = "MINIMUM", + Mode = "MODE", + Sum = "SUM", + Type = "TYPE", +} + +export enum TextMetrics { + Count = "COUNT", + Type = "TYPE", + TopOccurrences = "TOP_OCCURRENCES", +} + +export enum BooleanMetrics { + Count = "COUNT", + Type = "TYPE", + TotalTrue = "TOTAL_TRUE", + TotalFalse = "TOTAL_FALSE", + PercentageTrue = "PERCENTAGE_TRUE", + PercentageFalse = "PERCENTAGE_FALSE", +} + +export type Usage = { + requests: number; + requestTokens?: number; + responseTokens?: number; + totalTokens?: number; + details?: Record; +}; + +export type Source = { + objectId: string; + collection: string; +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..50085bd --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "NodeNext", + "declaration": true, + "outDir": "dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + } +}