feat: add metaphor tool

Travis Fischer 2023-05-26 16:50:45 -07:00
rodzic ab9de9a725
commit c788dcc968
9 zmienionych plików z 184 dodań i 65 usunięć

18
examples/tools.ts 100644
Wyświetl plik

@ -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()

Wyświetl plik

@ -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",

Wyświetl plik

@ -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

Wyświetl plik

@ -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'

Wyświetl plik

@ -30,7 +30,7 @@ export abstract class BaseLLMCallBuilder<
this._examples = options.examples
}
override input<U extends ZodRawShape | ZodTypeAny = TInput>(
input<U extends ZodRawShape | ZodTypeAny = TInput>(
inputSchema: U
): BaseLLMCallBuilder<U, TOutput, TModelParams> {
;(
@ -39,7 +39,7 @@ export abstract class BaseLLMCallBuilder<
return this as unknown as BaseLLMCallBuilder<U, TOutput, TModelParams>
}
override output<U extends ZodRawShape | ZodTypeAny = TOutput>(
output<U extends ZodRawShape | ZodTypeAny = TOutput>(
outputSchema: U
): BaseLLMCallBuilder<TInput, U, TModelParams> {
;(

Wyświetl plik

@ -38,15 +38,10 @@ export class OpenAIChatModelBuilder<
): Promise<
types.BaseChatCompletionResponse<types.openai.ChatCompletionResponse>
> {
const response = await this._client.createChatCompletion({
return this._client.createChatCompletion({
model: this._model,
...this._modelParams,
messages
})
return {
message: response.message,
response: response.response
}
}
}

Wyświetl plik

@ -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<MetaphorSearchResponse>()
}
}

Wyświetl plik

@ -18,20 +18,12 @@ export abstract class BaseTaskCallBuilder<
this._retryConfig = options.retryConfig
}
input<U extends ZodRawShape | ZodTypeAny = TInput>(
inputSchema: U
): BaseTaskCallBuilder<U, TOutput> {
;(this as unknown as BaseTaskCallBuilder<U, TOutput>)._inputSchema =
inputSchema
return this as unknown as BaseTaskCallBuilder<U, TOutput>
public get inputSchema(): TInput {
return this._inputSchema
}
output<U extends ZodRawShape | ZodTypeAny = TOutput>(
outputSchema: U
): BaseTaskCallBuilder<TInput, U> {
;(this as unknown as BaseTaskCallBuilder<TInput, U>)._outputSchema =
outputSchema
return this as unknown as BaseTaskCallBuilder<TInput, U>
public get outputSchema(): TOutput {
return this._outputSchema
}
retry(retryConfig: types.RetryConfig) {

Wyświetl plik

@ -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<MetaphorSearchToolOutput> {
// TODO: handle errors gracefully
input = this._inputSchema.parse(input)
return this._metaphorClient.search({
query: input.query,
numResults: input.numResults
})
}
}