feat: remove AIToolSet in favor of just AIFunctionSet

pull/643/head^2
Travis Fischer 2024-06-02 18:31:09 -05:00
rodzic 65e2e73a00
commit a5bf1736a9
16 zmienionych plików z 46 dodań i 165 usunięć

Wyświetl plik

@ -18,22 +18,20 @@ async function main() {
{ role: 'user', content: 'What is the weather in San Francisco?' } { role: 'user', content: 'What is the weather in San Francisco?' }
] ]
const tools = weather.tools
{ {
// First call to OpenAI to invoke the weather tool // First call to OpenAI to invoke the weather tool
const res = await openai.chat.completions.create({ const res = await openai.chat.completions.create({
messages, messages,
model: 'gpt-4o', model: 'gpt-4o',
temperature: 0, temperature: 0,
tools: tools.specs, tools: weather.functions.toolSpecs,
tool_choice: 'required' tool_choice: 'required'
}) })
const message = res.choices[0]?.message! const message = res.choices[0]?.message!
console.log(JSON.stringify(message, null, 2)) console.log(JSON.stringify(message, null, 2))
assert(message.tool_calls?.[0]?.function?.name === 'get_current_weather') assert(message.tool_calls?.[0]?.function?.name === 'get_current_weather')
const fn = tools.get('get_current_weather')!.function const fn = weather.functions.get('get_current_weather')!
assert(fn) assert(fn)
const toolParams = message.tool_calls[0].function.arguments const toolParams = message.tool_calls[0].function.arguments
@ -53,7 +51,7 @@ async function main() {
messages, messages,
model: 'gpt-4o', model: 'gpt-4o',
temperature: 0, temperature: 0,
tools: tools.specs tools: weather.functions.toolSpecs
}) })
const message = res.choices[0].message const message = res.choices[0].message
console.log(JSON.stringify(message, null, 2)) console.log(JSON.stringify(message, null, 2))

Wyświetl plik

@ -45,7 +45,6 @@
"scripts": { "scripts": {
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"build": "tsc", "build": "tsc",
"dev": "tsup --watch",
"clean": "del dist", "clean": "del dist",
"prebuild": "run-s clean", "prebuild": "run-s clean",
"predev": "run-s clean", "predev": "run-s clean",

Wyświetl plik

@ -1,13 +1,19 @@
import type { AIToolSet } from './ai-tool-set.js'
import type * as types from './types.ts' import type * as types from './types.ts'
import { AIFunctionsProvider } from './fns.js'
export class AIFunctionSet implements Iterable<types.AIFunction> { export class AIFunctionSet implements Iterable<types.AIFunction> {
protected readonly _map: Map<string, types.AIFunction> protected readonly _map: Map<string, types.AIFunction>
constructor(functions?: readonly types.AIFunction[]) { constructor(aiFunctionLikeObjects?: types.AIFunctionLike[]) {
this._map = new Map( const fns = aiFunctionLikeObjects?.flatMap((fn) =>
functions ? functions.map((fn) => [fn.spec.name, fn]) : null fn instanceof AIFunctionsProvider
? [...fn.functions]
: fn instanceof AIFunctionSet
? [...fn]
: [fn]
) )
this._map = new Map(fns ? fns.map((fn) => [fn.spec.name, fn]) : null)
} }
get size(): number { get size(): number {
@ -76,12 +82,4 @@ export class AIFunctionSet implements Iterable<types.AIFunction> {
[Symbol.iterator](): Iterator<types.AIFunction> { [Symbol.iterator](): Iterator<types.AIFunction> {
return this.entries return this.entries
} }
static fromAIToolSet(tools: AIToolSet): AIFunctionSet {
return new AIFunctionSet(
Array.from(tools)
.filter((tool) => tool.spec.type === 'function')
.map((tool) => tool.function)
)
}
} }

Wyświetl plik

@ -1,100 +0,0 @@
import type * as types from './types.js'
import { AIFunctionSet } from './ai-function-set.js'
export class AIToolSet implements Iterable<types.AITool> {
protected _map: Map<string, types.AITool>
constructor(tools?: readonly types.AITool[]) {
this._map = new Map(
tools ? tools.map((tool) => [tool.spec.function.name, tool]) : []
)
}
get size(): number {
return this._map.size
}
add(tool: types.AITool): this {
this._map.set(tool.spec.function.name, tool)
return this
}
get(name: string): types.AITool | undefined {
return this._map.get(name)
}
set(name: string, tool: types.AITool): this {
this._map.set(name, tool)
return this
}
has(name: string): boolean {
return this._map.has(name)
}
clear(): void {
this._map.clear()
}
delete(name: string): boolean {
return this._map.delete(name)
}
pick(...keys: string[]): AIToolSet {
const keysToIncludeSet = new Set(keys)
return new AIToolSet(
Array.from(this).filter((tool) =>
keysToIncludeSet.has(tool.spec.function.name)
)
)
}
omit(...keys: string[]): AIToolSet {
const keysToExcludeSet = new Set(keys)
return new AIToolSet(
Array.from(this).filter(
(tool) => !keysToExcludeSet.has(tool.spec.function.name)
)
)
}
map<T>(fn: (fn: types.AITool) => T): T[] {
return [...this.entries].map(fn)
}
get functionSpecs(): types.AIFunctionSpec[] {
return this.map((fn) => fn.function.spec)
}
get specs(): types.AIToolSpec[] {
return this.map((fn) => fn.spec)
}
get entries(): IterableIterator<types.AITool> {
return this._map.values()
}
[Symbol.iterator](): Iterator<types.AITool> {
return this.entries
}
static fromAIFunctionSet(functions: AIFunctionSet): AIToolSet {
return new AIToolSet(
Array.from(functions).map((fn) => ({
function: fn,
spec: {
type: 'function' as const,
function: fn.spec
}
}))
)
}
static fromFunctions(functions: types.AIFunction[]): AIToolSet {
return AIToolSet.fromAIFunctionSet(new AIFunctionSet(functions))
}
static fromTools(tools: types.AITool[]): AIToolSet {
return new AIToolSet(tools)
}
}

Wyświetl plik

@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { z } from 'zod' import { z } from 'zod'
import { createAIFunction } from './ai-function.js' import { createAIFunction } from './create-ai-function.js'
const fullName = createAIFunction( const fullName = createAIFunction(
{ {

Wyświetl plik

@ -4,7 +4,6 @@ import type { z } from 'zod'
import type * as types from './types.js' import type * as types from './types.js'
import { AIFunctionSet } from './ai-function-set.js' import { AIFunctionSet } from './ai-function-set.js'
import { AIToolSet } from './ai-tool-set.js'
import { createAIFunction } from './create-ai-function.js' import { createAIFunction } from './create-ai-function.js'
import { assert } from './utils.js' import { assert } from './utils.js'
@ -15,18 +14,9 @@ export interface Invocable {
methodName: string methodName: string
} }
export abstract class AIToolsProvider { export abstract class AIFunctionsProvider {
private _tools?: AIToolSet
private _functions?: AIFunctionSet private _functions?: AIFunctionSet
get tools(): AIToolSet {
if (!this._tools) {
this._tools = AIToolSet.fromAIFunctionSet(this.functions)
}
return this._tools
}
get functions(): AIFunctionSet { get functions(): AIFunctionSet {
if (!this._functions) { if (!this._functions) {
const metadata = this.constructor[Symbol.metadata] const metadata = this.constructor[Symbol.metadata]

Wyświetl plik

@ -1,5 +1,5 @@
export * from './ai-function-set.js' export * from './ai-function-set.js'
export * from './ai-tool-set.js' export * from './create-ai-function.js'
export * from './create-ai-function.js' export * from './create-ai-function.js'
export * from './errors.js' export * from './errors.js'
export * from './fns.js' export * from './fns.js'

Wyświetl plik

@ -1,17 +1,17 @@
import { tool } from 'ai' import { tool } from 'ai'
import type { AIFunctionSet } from '../ai-function-set.js' import type { AIFunctionLike } from '../types.js'
import { AIToolsProvider } from '../fns.js' import { AIFunctionSet } from '../ai-function-set.js'
/** /**
* Converts a set of Agentic stdlib AI functions to an object compatible with * Converts a set of Agentic stdlib AI functions to an object compatible with
* the Vercel AI SDK's `tools` parameter. * the Vercel AI SDK's `tools` parameter.
*/ */
export function createAISDKTools(tools: AIToolsProvider | AIFunctionSet) { export function createAISDKTools(...aiFunctionLikeTools: AIFunctionLike[]) {
const fns = tools instanceof AIToolsProvider ? tools.functions : tools const fns = new AIFunctionSet(aiFunctionLikeTools)
return Object.fromEntries( return Object.fromEntries(
[...fns].map((fn) => [ fns.map((fn) => [
fn.spec.name, fn.spec.name,
tool({ tool({
description: fn.spec.description, description: fn.spec.description,

Wyświetl plik

@ -1,14 +1,16 @@
import { createAIFunction } from '@dexaai/dexter' import { createAIFunction } from '@dexaai/dexter'
import type { AIFunctionSet } from '../ai-function-set.js' import type { AIFunctionLike } from '../types.js'
import { AIToolsProvider } from '../fns.js' import { AIFunctionSet } from '../ai-function-set.js'
/** /**
* Converts a set of Agentic stdlib AI functions to an array of Dexter- * Converts a set of Agentic stdlib AI functions to an array of Dexter-
* compatible AI functions. * compatible AI functions.
*/ */
export function createDexterFunctions(input: AIToolsProvider | AIFunctionSet) { export function createDexterFunctions(
const fns = input instanceof AIToolsProvider ? input.functions : input ...aiFunctionLikeTools: AIFunctionLike[]
) {
const fns = new AIFunctionSet(aiFunctionLikeTools)
return fns.map((fn) => return fns.map((fn) =>
createAIFunction( createAIFunction(

Wyświetl plik

@ -1,15 +1,15 @@
import { defineTool } from '@genkit-ai/ai' import { defineTool } from '@genkit-ai/ai'
import { z } from 'zod' import { z } from 'zod'
import type { AIFunctionSet } from '../ai-function-set.js' import type { AIFunctionLike } from '../types.js'
import { AIToolsProvider } from '../fns.js' import { AIFunctionSet } from '../ai-function-set.js'
/** /**
* Converts a set of Agentic stdlib AI functions to an array of Genkit- * Converts a set of Agentic stdlib AI functions to an array of Genkit-
* compatible tools. * compatible tools.
*/ */
export function createGenkitTools(input: AIToolsProvider | AIFunctionSet) { export function createGenkitTools(...aiFunctionLikeTools: AIFunctionLike[]) {
const fns = input instanceof AIToolsProvider ? input.functions : input const fns = new AIFunctionSet(aiFunctionLikeTools)
return fns.map((fn) => return fns.map((fn) =>
defineTool( defineTool(

Wyświetl plik

@ -1,15 +1,15 @@
import { DynamicStructuredTool } from '@langchain/core/tools' import { DynamicStructuredTool } from '@langchain/core/tools'
import type { AIFunctionSet } from '../ai-function-set.js' import type { AIFunctionLike } from '../types.js'
import { AIToolsProvider } from '../fns.js' import { AIFunctionSet } from '../ai-function-set.js'
import { stringifyForModel } from '../stringify-for-model.js' import { stringifyForModel } from '../stringify-for-model.js'
/** /**
* Converts a set of Agentic stdlib AI functions to an array of LangChain- * Converts a set of Agentic stdlib AI functions to an array of LangChain-
* compatible tools. * compatible tools.
*/ */
export function createLangChainTools(input: AIToolsProvider | AIFunctionSet) { export function createLangChainTools(...aiFunctionLikeTools: AIFunctionLike[]) {
const fns = input instanceof AIToolsProvider ? input.functions : input const fns = new AIFunctionSet(aiFunctionLikeTools)
return fns.map( return fns.map(
(fn) => (fn) =>

Wyświetl plik

@ -1,7 +1,7 @@
import defaultKy, { type KyInstance } from 'ky' import defaultKy, { type KyInstance } from 'ky'
import { z } from 'zod' import { z } from 'zod'
import { aiFunction, AIToolsProvider } from '../fns.js' import { aiFunction, AIFunctionsProvider } from '../fns.js'
import { assert, getEnv } from '../utils.js' import { assert, getEnv } from '../utils.js'
/** /**
@ -633,7 +633,7 @@ export namespace serpapi {
* *
* @see https://serpapi.com/search-api * @see https://serpapi.com/search-api
*/ */
export class SerpAPIClient extends AIToolsProvider { export class SerpAPIClient extends AIFunctionsProvider {
protected ky: KyInstance protected ky: KyInstance
protected apiKey: string protected apiKey: string
protected apiBaseUrl: string protected apiBaseUrl: string

Wyświetl plik

@ -1,7 +1,7 @@
import defaultKy, { type KyInstance } from 'ky' import defaultKy, { type KyInstance } from 'ky'
import { z } from 'zod' import { z } from 'zod'
import { aiFunction, AIToolsProvider } from '../fns.js' import { aiFunction, AIFunctionsProvider } from '../fns.js'
import { assert, getEnv } from '../utils.js' import { assert, getEnv } from '../utils.js'
export namespace serper { export namespace serper {
@ -194,7 +194,7 @@ export namespace serper {
* *
* @see https://serper.dev * @see https://serper.dev
*/ */
export class SerperClient extends AIToolsProvider { export class SerperClient extends AIFunctionsProvider {
readonly ky: KyInstance readonly ky: KyInstance
readonly apiKey: string readonly apiKey: string
readonly apiBaseUrl: string readonly apiBaseUrl: string

Wyświetl plik

@ -1,11 +0,0 @@
import { expect, test } from 'vitest'
import { WeatherClient } from './weather-client.js'
test('WeatherClient.functions', () => {
const weather = new WeatherClient({
apiKey: 'sk-test'
})
expect(weather.functions.get('get_current_weather')).toBeTruthy()
})

Wyświetl plik

@ -1,7 +1,7 @@
import defaultKy, { type KyInstance } from 'ky' import defaultKy, { type KyInstance } from 'ky'
import { z } from 'zod' import { z } from 'zod'
import { aiFunction, AIToolsProvider } from '../fns.js' import { aiFunction, AIFunctionsProvider } from '../fns.js'
import { assert, getEnv } from '../utils.js' import { assert, getEnv } from '../utils.js'
export namespace weatherapi { export namespace weatherapi {
@ -74,7 +74,7 @@ export namespace weatherapi {
} }
} }
export class WeatherClient extends AIToolsProvider { export class WeatherClient extends AIFunctionsProvider {
readonly ky: KyInstance readonly ky: KyInstance
readonly apiKey: string readonly apiKey: string
readonly apiBaseUrl: string readonly apiBaseUrl: string

Wyświetl plik

@ -1,6 +1,9 @@
import type { Jsonifiable } from 'type-fest' import type { Jsonifiable } from 'type-fest'
import type { z } from 'zod' import type { z } from 'zod'
import type { AIFunctionSet } from './ai-function-set.js'
import type { AIFunctionsProvider } from './fns.js'
export type { KyInstance } from 'ky' export type { KyInstance } from 'ky'
export type { ThrottledFunction } from 'p-throttle' export type { ThrottledFunction } from 'p-throttle'
@ -28,6 +31,8 @@ export type AIFunctionImpl<Return> = Omit<
'name' | 'toString' | 'arguments' | 'caller' | 'prototype' | 'length' 'name' | 'toString' | 'arguments' | 'caller' | 'prototype' | 'length'
> >
export type AIFunctionLike = AIFunctionsProvider | AIFunction | AIFunctionSet
/** /**
* A function meant to be used with LLM function calling. * A function meant to be used with LLM function calling.
*/ */