kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: remove AIToolSet in favor of just AIFunctionSet
rodzic
65e2e73a00
commit
a5bf1736a9
|
@ -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))
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
{
|
12
src/fns.ts
12
src/fns.ts
|
@ -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]
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
})
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
Ładowanie…
Reference in New Issue