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?' }
|
{ 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))
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 { 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(
|
||||||
{
|
{
|
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 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]
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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) =>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 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
|
||||||
|
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
Ładowanie…
Reference in New Issue