diff --git a/examples/tools.ts b/examples/tools.ts new file mode 100644 index 00000000..aebaabc6 --- /dev/null +++ b/examples/tools.ts @@ -0,0 +1,18 @@ +import dotenv from 'dotenv-safe' +import { OpenAIClient } from 'openai-fetch' +import { z } from 'zod' + +import { Agentic, MetaphorSearchTool } from '../src' + +dotenv.config() + +async function main() { + const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! }) + const $ = new Agentic({ openai }) + + const metaphorSearch = new MetaphorSearchTool() + const results = await metaphorSearch.call({ query: 'kittens', numResults: 5 }) + console.log(results) +} + +main() diff --git a/package.json b/package.json index 64085331..a25ace0f 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,12 @@ "@dqbd/tiktoken": "^1.0.7", "dotenv-safe": "^8.2.0", "jsonrepair": "^3.1.0", + "ky": "^0.33.3", "mustache": "^4.2.0", - "openai-fetch": "^1.2.1", + "openai-fetch": "^1.3.0", "p-map": "^6.0.0", "ts-dedent": "^2.2.0", - "type-fest": "^3.10.0", + "type-fest": "^3.11.0", "zod": "^3.21.4", "zod-to-ts": "^1.1.4", "zod-validation-error": "^1.3.0" @@ -50,7 +51,7 @@ "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.1.1", "@types/mustache": "^4.2.2", - "@types/node": "^20.2.0", + "@types/node": "^20.2.5", "ava": "^5.3.0", "del-cli": "^5.0.0", "husky": "^8.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91cfa83f..b737b9f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,12 +10,15 @@ dependencies: jsonrepair: specifier: ^3.1.0 version: 3.1.0 + ky: + specifier: ^0.33.3 + version: 0.33.3 mustache: specifier: ^4.2.0 version: 4.2.0 openai-fetch: - specifier: ^1.2.1 - version: 1.2.1 + specifier: ^1.3.0 + version: 1.3.0 p-map: specifier: ^6.0.0 version: 6.0.0 @@ -23,8 +26,8 @@ dependencies: specifier: ^2.2.0 version: 2.2.0 type-fest: - specifier: ^3.10.0 - version: 3.10.0(typescript@5.0.4) + specifier: ^3.11.0 + version: 3.11.0 zod: specifier: ^3.21.4 version: 3.21.4 @@ -43,8 +46,8 @@ devDependencies: specifier: ^4.2.2 version: 4.2.2 '@types/node': - specifier: ^20.2.0 - version: 20.2.0 + specifier: ^20.2.5 + version: 20.2.5 ava: specifier: ^5.3.0 version: 5.3.0 @@ -91,8 +94,8 @@ packages: source-map: 0.5.7 dev: true - /@babel/helper-environment-visitor@7.21.5: - resolution: {integrity: sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==} + /@babel/helper-environment-visitor@7.22.1: + resolution: {integrity: sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==} engines: {node: '>=6.9.0'} dev: true @@ -100,22 +103,22 @@ packages: resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.5 + '@babel/template': 7.21.9 + '@babel/types': 7.22.0 dev: true /@babel/helper-hoist-variables@7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.0 dev: true /@babel/helper-split-export-declaration@7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.0 dev: true /@babel/helper-string-parser@7.21.5: @@ -137,21 +140,21 @@ packages: js-tokens: 4.0.0 dev: true - /@babel/parser@7.21.8: - resolution: {integrity: sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==} + /@babel/parser@7.22.0: + resolution: {integrity: sha512-DA65VCJRetcFmJnt9/hEmRvXNCwk0V86dxG6p6N13hzDazaLRjGdTGPGgjxZOtLuFgWzOSRX4grybmRXwQ9bSg==} engines: {node: '>=6.0.0'} hasBin: true dependencies: '@babel/types': 7.17.0 dev: true - /@babel/template@7.20.7: - resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} + /@babel/template@7.21.9: + resolution: {integrity: sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.21.4 - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 + '@babel/parser': 7.22.0 + '@babel/types': 7.22.0 dev: true /@babel/traverse@7.17.3: @@ -160,11 +163,11 @@ packages: dependencies: '@babel/code-frame': 7.21.4 '@babel/generator': 7.17.7 - '@babel/helper-environment-visitor': 7.21.5 + '@babel/helper-environment-visitor': 7.22.1 '@babel/helper-function-name': 7.21.0 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.0 '@babel/types': 7.17.0 debug: 4.3.4 globals: 11.12.0 @@ -180,8 +183,8 @@ packages: to-fast-properties: 2.0.0 dev: true - /@babel/types@7.21.5: - resolution: {integrity: sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==} + /@babel/types@7.22.0: + resolution: {integrity: sha512-NtXlm3f6cNWIv003cETdlz9sss0VMNtplyatFohxWPz90AbwuhCbHbQopkGis6bG1vOunDLN0FF/4Uv5i8LFZQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.21.5 @@ -477,7 +480,7 @@ packages: optional: true dependencies: '@babel/generator': 7.17.7 - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.0 '@babel/traverse': 7.17.3 '@babel/types': 7.17.0 javascript-natural-sort: 0.7.1 @@ -495,8 +498,8 @@ packages: resolution: {integrity: sha512-MUSpfpW0yZbTgjekDbH0shMYBUD+X/uJJJMm9LXN1d5yjl5lCY1vN/eWKD6D1tOtjA6206K0zcIPnUaFMurdNA==} dev: true - /@types/node@20.2.0: - resolution: {integrity: sha512-3iD2jaCCziTx04uudpJKwe39QxXgSUnpxXSvRQjRvHPxFQfmfP4NXIm/NURVeNlTCc+ru4WqjYGTmpXrW9uMlw==} + /@types/node@20.2.5: + resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==} dev: true /@types/normalize-package-data@2.4.1: @@ -1785,7 +1788,7 @@ packages: object-inspect: 1.12.3 pidtree: 0.6.0 string-argv: 0.3.2 - yaml: 2.2.2 + yaml: 2.3.1 transitivePeerDependencies: - enquirer - supports-color @@ -2106,8 +2109,8 @@ packages: mimic-fn: 4.0.0 dev: true - /openai-fetch@1.2.1: - resolution: {integrity: sha512-/W+HqOz4KJ3vd+P1nvHiN2fT6cMf5bOJbkb6+xfI+MjX+gxFy9DSKj97y+L6LSA527/wEVk7QSapJBji3eYmfw==} + /openai-fetch@1.3.0: + resolution: {integrity: sha512-p3U4bZzAqfG0kkN9Z2fze0jqL2cZkbUSPI7oyzaVoms4T/Rovlar+ZaUf8evStgFH6p7WoBmeqnH6tt/Y50/+Q==} dependencies: ky: 0.33.3 zod: 3.21.4 @@ -2433,8 +2436,8 @@ packages: glob: 7.2.3 dev: true - /rollup@3.22.0: - resolution: {integrity: sha512-imsigcWor5Y/dC0rz2q0bBt9PabcL3TORry2hAa6O6BuMvY71bqHyfReAz5qyAqiQATD1m70qdntqBfBQjVWpQ==} + /rollup@3.23.0: + resolution: {integrity: sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -2450,7 +2453,7 @@ packages: /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - tslib: 2.5.1 + tslib: 2.5.2 dev: true /safe-regex-test@1.0.0: @@ -2812,8 +2815,8 @@ packages: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true - /tslib@2.5.1: - resolution: {integrity: sha512-KaI6gPil5m9vF7DKaoXxx1ia9fxS4qG5YveErRRVknPDXXriu5M8h48YRjB6h5ZUOKuAKlSJYb0GaDe8I39fRw==} + /tslib@2.5.2: + resolution: {integrity: sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==} dev: true /tsup@6.7.0(typescript@5.0.4): @@ -2842,7 +2845,7 @@ packages: joycon: 3.1.1 postcss-load-config: 3.1.4 resolve-from: 5.0.0 - rollup: 3.22.0 + rollup: 3.23.0 source-map: 0.8.0-beta.0 sucrase: 3.32.0 tree-kill: 1.2.2 @@ -2878,13 +2881,9 @@ packages: engines: {node: '>=10'} dev: true - /type-fest@3.10.0(typescript@5.0.4): - resolution: {integrity: sha512-hmAPf1datm+gt3c2mvu0sJyhFy6lTkIGf0GzyaZWxRLnabQfPUqg6tF95RPg6sLxKI7nFLGdFxBcf2/7+GXI+A==} + /type-fest@3.11.0: + resolution: {integrity: sha512-JaPw5U9ixP0XcpUbQoVSbxSDcK/K4nww20C3kjm9yE6cDRRhptU28AH60VWf9ltXmCrIfIbtt9J+2OUk2Uqiaw==} engines: {node: '>=14.16'} - peerDependencies: - typescript: '>=4.7.0' - dependencies: - typescript: 5.0.4 dev: false /typed-array-length@1.0.4: @@ -3014,8 +3013,8 @@ packages: engines: {node: '>= 6'} dev: true - /yaml@2.2.2: - resolution: {integrity: sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==} + /yaml@2.3.1: + resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} engines: {node: '>= 14'} dev: true diff --git a/src/index.ts b/src/index.ts index e86266ab..866e4197 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,8 @@ export * from './agentic' +export * from './task' export * from './llm' export * from './openai' export * from './tokenizer' + +export * from './services/metaphor' +export * from './tools/metaphor' diff --git a/src/llm.ts b/src/llm.ts index e72d21d5..c30722bb 100644 --- a/src/llm.ts +++ b/src/llm.ts @@ -30,7 +30,7 @@ export abstract class BaseLLMCallBuilder< this._examples = options.examples } - override input( + input( inputSchema: U ): BaseLLMCallBuilder { ;( @@ -39,7 +39,7 @@ export abstract class BaseLLMCallBuilder< return this as unknown as BaseLLMCallBuilder } - override output( + output( outputSchema: U ): BaseLLMCallBuilder { ;( diff --git a/src/openai.ts b/src/openai.ts index 6fe0e864..e9b5bb48 100644 --- a/src/openai.ts +++ b/src/openai.ts @@ -38,15 +38,10 @@ export class OpenAIChatModelBuilder< ): Promise< types.BaseChatCompletionResponse > { - const response = await this._client.createChatCompletion({ + return this._client.createChatCompletion({ model: this._model, ...this._modelParams, messages }) - - return { - message: response.message, - response: response.response - } } } diff --git a/src/services/metaphor.ts b/src/services/metaphor.ts new file mode 100644 index 00000000..4f4be8b4 --- /dev/null +++ b/src/services/metaphor.ts @@ -0,0 +1,49 @@ +import ky from 'ky' + +export type MetaphorSearchResult = { + author?: string | null + dateCreated?: string + score: number + title: string + url: string +} + +export type MetaphorSearchResponse = { + results: MetaphorSearchResult[] +} + +export class MetaphorClient { + apiKey: string + baseUrl: string + + constructor({ + apiKey = process.env.METAPHOR_API_KEY, + baseUrl = 'https://api.metaphor.systems' + }: { + apiKey?: string + baseUrl?: string + } = {}) { + this.apiKey = apiKey + this.baseUrl = baseUrl + } + + async search({ + query, + numResults = 10 + }: { + query: string + numResults?: number + }) { + return ky + .post(`${this.baseUrl}/search`, { + headers: { + 'x-api-key': this.apiKey + }, + json: { + query, + numResults + } + }) + .json() + } +} diff --git a/src/task.ts b/src/task.ts index 8af8fc98..4170d2a4 100644 --- a/src/task.ts +++ b/src/task.ts @@ -18,20 +18,12 @@ export abstract class BaseTaskCallBuilder< this._retryConfig = options.retryConfig } - input( - inputSchema: U - ): BaseTaskCallBuilder { - ;(this as unknown as BaseTaskCallBuilder)._inputSchema = - inputSchema - return this as unknown as BaseTaskCallBuilder + public get inputSchema(): TInput { + return this._inputSchema } - output( - outputSchema: U - ): BaseTaskCallBuilder { - ;(this as unknown as BaseTaskCallBuilder)._outputSchema = - outputSchema - return this as unknown as BaseTaskCallBuilder + public get outputSchema(): TOutput { + return this._outputSchema } retry(retryConfig: types.RetryConfig) { diff --git a/src/tools/metaphor.ts b/src/tools/metaphor.ts new file mode 100644 index 00000000..a3251ea8 --- /dev/null +++ b/src/tools/metaphor.ts @@ -0,0 +1,61 @@ +import { z } from 'zod' + +import { MetaphorClient } from '../services/metaphor' +import { BaseTaskCallBuilder } from '../task' + +export const MetaphorSearchToolInputSchema = z.object({ + query: z.string(), + numResults: z.number().optional() +}) + +export type MetaphorSearchToolInput = z.infer< + typeof MetaphorSearchToolInputSchema +> + +export const MetaphorSearchToolOutputSchema = z.object({ + results: z.array( + z.object({ + author: z.string().optional(), + dateCreated: z.string().optional(), + score: z.number(), + title: z.string(), + URL: z.string() + }) + ) +}) + +export type MetaphorSearchToolOutput = z.infer< + typeof MetaphorSearchToolOutputSchema +> + +export class MetaphorSearchTool extends BaseTaskCallBuilder< + typeof MetaphorSearchToolInputSchema, + typeof MetaphorSearchToolOutputSchema +> { + _metaphorClient: MetaphorClient + + constructor({ + metaphorClient = new MetaphorClient() + }: { + metaphorClient?: MetaphorClient + } = {}) { + super({ + inputSchema: MetaphorSearchToolInputSchema, + outputSchema: MetaphorSearchToolOutputSchema + }) + + this._metaphorClient = metaphorClient + } + + override async call( + input: MetaphorSearchToolInput + ): Promise { + // TODO: handle errors gracefully + input = this._inputSchema.parse(input) + + return this._metaphorClient.search({ + query: input.query, + numResults: input.numResults + }) + } +}