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?' }
]
const tools = weather.tools
{
// First call to OpenAI to invoke the weather tool
const res = await openai.chat.completions.create({
messages,
model: 'gpt-4o',
temperature: 0,
tools: tools.specs,
tools: weather.functions.toolSpecs,
tool_choice: 'required'
})
const message = res.choices[0]?.message!
console.log(JSON.stringify(message, null, 2))
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)
const toolParams = message.tool_calls[0].function.arguments
@ -53,7 +51,7 @@ async function main() {
messages,
model: 'gpt-4o',
temperature: 0,
tools: tools.specs
tools: weather.functions.toolSpecs
})
const message = res.choices[0].message
console.log(JSON.stringify(message, null, 2))

Wyświetl plik

@ -45,7 +45,6 @@
"scripts": {
"preinstall": "npx only-allow pnpm",
"build": "tsc",
"dev": "tsup --watch",
"clean": "del dist",
"prebuild": "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 { AIFunctionsProvider } from './fns.js'
export class AIFunctionSet implements Iterable<types.AIFunction> {
protected readonly _map: Map<string, types.AIFunction>
constructor(functions?: readonly types.AIFunction[]) {
this._map = new Map(
functions ? functions.map((fn) => [fn.spec.name, fn]) : null
constructor(aiFunctionLikeObjects?: types.AIFunctionLike[]) {
const fns = aiFunctionLikeObjects?.flatMap((fn) =>
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 {
@ -76,12 +82,4 @@ export class AIFunctionSet implements Iterable<types.AIFunction> {
[Symbol.iterator](): Iterator<types.AIFunction> {
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 { z } from 'zod'
import { createAIFunction } from './ai-function.js'
import { createAIFunction } from './create-ai-function.js'
const fullName = createAIFunction(
{

Wyświetl plik

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

Wyświetl plik

@ -1,5 +1,5 @@
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 './errors.js'
export * from './fns.js'

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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