feat: refactoring core

old-agentic-v1^2
Travis Fischer 2023-05-31 18:53:09 -07:00
rodzic 8c7b34f0dc
commit ed4ea41c51
21 zmienionych plików z 150 dodań i 98 usunięć

Wyświetl plik

@ -6,4 +6,3 @@
# ------------------------------------------------------------------------------
OPENAI_API_KEY=
METAPHOR_API_KEY=

Wyświetl plik

@ -1,11 +1,9 @@
import dotenv from 'dotenv-safe'
import 'dotenv/config'
import { OpenAIClient } from 'openai-fetch'
import { z } from 'zod'
import { Agentic } from '../src'
dotenv.config()
async function main() {
const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! })
const $ = new Agentic({ openai })

Wyświetl plik

@ -1,12 +1,10 @@
import dotenv from 'dotenv-safe'
import 'dotenv/config'
import { OpenAIClient } from 'openai-fetch'
import { z } from 'zod'
import { Agentic } from '../src/llm'
import { Agentic } from '../src'
import { getProblems } from './fixtures/calc'
dotenv.config()
export async function calcEval() {
const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! })
const $ = new Agentic({ openai })

Wyświetl plik

@ -1,11 +1,9 @@
import dotenv from 'dotenv-safe'
import 'dotenv/config'
import { OpenAIClient } from 'openai-fetch'
import { z } from 'zod'
import { Agentic } from '../src'
dotenv.config()
export async function equationProducer() {
const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! })
const $ = new Agentic({ openai })
@ -31,11 +29,13 @@ export async function equationProducer() {
`You are an expert math teacher. Think step by step, and give me the equation for the following math problem: \n\n{{question}}`
)
.input(z.object({ question: z.string() }))
.output({
question: z.string(),
equation: z.string(),
predictedAnswer: z.number()
})
.output(
z.object({
question: z.string(),
equation: z.string(),
predictedAnswer: z.number()
})
)
.examples(examples)
// .assert(
// (output) =>

Wyświetl plik

@ -1,11 +1,9 @@
import dotenv from 'dotenv-safe'
import 'dotenv/config'
import { OpenAIClient } from 'openai-fetch'
import { z } from 'zod'
import { Agentic } from '../src'
dotenv.config()
async function main() {
const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! })
const ai = new Agentic({ openai })

Wyświetl plik

@ -1,12 +1,10 @@
import dotenv from 'dotenv-safe'
import 'dotenv/config'
import { OpenAIClient } from 'openai-fetch'
import { z } from 'zod'
import { Agentic } from '../src'
import { summaryAgent } from './summary'
dotenv.config()
async function main() {
const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! })
const $ = new Agentic({ openai })

Wyświetl plik

@ -1,11 +1,9 @@
import dotenv from 'dotenv-safe'
import 'dotenv/config'
import { OpenAIClient } from 'openai-fetch'
import { z } from 'zod'
import { Agentic } from '../src'
dotenv.config()
export async function main() {
const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! })
const $ = new Agentic({ openai })

Wyświetl plik

@ -1,11 +1,9 @@
import dotenv from 'dotenv-safe'
import 'dotenv/config'
import { OpenAIClient } from 'openai-fetch'
import { z } from 'zod'
import { Agentic, MetaphorSearchTool } from '../src'
dotenv.config()
async function main() {
const metaphorSearch = new MetaphorSearchTool()

Wyświetl plik

@ -1,11 +1,9 @@
import dotenv from 'dotenv-safe'
import 'dotenv/config'
import { OpenAIClient } from 'openai-fetch'
import { z } from 'zod'
import { Agentic } from '../src'
dotenv.config()
async function main() {
const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! })
const $ = new Agentic({ openai })

Wyświetl plik

@ -1,11 +1,9 @@
import dotenv from 'dotenv-safe'
import 'dotenv/config'
import { OpenAIClient } from 'openai-fetch'
import { z } from 'zod'
import { Agentic } from '../src'
dotenv.config()
export async function main() {
const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! })
const $ = new Agentic({ openai })

Wyświetl plik

@ -1,11 +1,9 @@
import dotenv from 'dotenv-safe'
import 'dotenv/config'
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 })

Wyświetl plik

@ -35,9 +35,9 @@
"test:prettier": "prettier '**/*.{js,jsx,ts,tsx}' --check"
},
"dependencies": {
"@dqbd/tiktoken": "^1.0.7",
"dotenv-safe": "^8.2.0",
"dotenv": "^16.1.3",
"handlebars": "^4.7.7",
"js-tiktoken": "^1.0.6",
"jsonrepair": "^3.1.0",
"ky": "^0.33.3",
"openai-fetch": "^1.3.0",

Wyświetl plik

@ -1,15 +1,15 @@
lockfileVersion: '6.0'
dependencies:
'@dqbd/tiktoken':
specifier: ^1.0.7
version: 1.0.7
dotenv-safe:
specifier: ^8.2.0
version: 8.2.0
dotenv:
specifier: ^16.1.3
version: 16.1.3
handlebars:
specifier: ^4.7.7
version: 4.7.7
js-tiktoken:
specifier: ^1.0.6
version: 1.0.6
jsonrepair:
specifier: ^3.1.0
version: 3.1.0
@ -192,10 +192,6 @@ packages:
to-fast-properties: 2.0.0
dev: true
/@dqbd/tiktoken@1.0.7:
resolution: {integrity: sha512-bhR5k5W+8GLzysjk8zTMVygQZsgvf7W1F0IlL4ZQ5ugjo5rCyiwGM5d8DYriXspytfu98tv59niang3/T+FoDw==}
dev: false
/@esbuild-kit/cjs-loader@2.4.2:
resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==}
dependencies:
@ -686,6 +682,10 @@ packages:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true
/base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: false
/binary-extensions@2.2.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
engines: {node: '>=8'}
@ -1024,15 +1024,9 @@ packages:
path-type: 4.0.0
dev: true
/dotenv-safe@8.2.0:
resolution: {integrity: sha512-uWwWWdUQkSs5a3mySDB22UtNwyEYi0JtEQu+vDzIqr9OjbDdC2Ip13PnSpi/fctqlYmzkxCeabiyCAOROuAIaA==}
dependencies:
dotenv: 8.6.0
dev: false
/dotenv@8.6.0:
resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==}
engines: {node: '>=10'}
/dotenv@16.1.3:
resolution: {integrity: sha512-FYssxsmCTtKL72fGBSvb1K9dRz0/VZeWqFme/vSb7r7323x4CRaHu4LvQ5JG3+s6yt2YPbBrkpiEODktfyjI9A==}
engines: {node: '>=12'}
dev: false
/eastasianwidth@0.2.0:
@ -1730,6 +1724,12 @@ packages:
engines: {node: '>= 0.8'}
dev: true
/js-tiktoken@1.0.6:
resolution: {integrity: sha512-lxHntEupgjWvSh37WxpAW4XN6UBXBtFJOpZZq5HN5oNjDfN7L/iJhHOKjyL/DFtuYXUwn5jfTciLtOWpgQmHjQ==}
dependencies:
base64-js: 1.5.1
dev: false
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: true

Wyświetl plik

@ -1,5 +1,6 @@
import { jsonrepair } from 'jsonrepair'
import { dedent } from 'ts-dedent'
import { type SetRequired } from 'type-fest'
import { ZodRawShape, ZodTypeAny, z } from 'zod'
import { printNode, zodToTs } from 'zod-to-ts'
@ -12,18 +13,29 @@ import {
} from './utils'
export abstract class BaseLLMCallBuilder<
TInput extends ZodRawShape | ZodTypeAny = ZodTypeAny,
TInput extends ZodRawShape | ZodTypeAny = z.ZodVoid,
TOutput extends ZodRawShape | ZodTypeAny = z.ZodType<string>,
TModelParams extends Record<string, any> = Record<string, any>
> extends BaseTaskCallBuilder<TInput, TOutput> {
protected _inputSchema: TInput | undefined
protected _outputSchema: TOutput | undefined
protected _provider: string
protected _model: string
protected _modelParams: TModelParams
protected _examples: types.LLMExample[]
protected _modelParams: TModelParams | undefined
protected _examples: types.LLMExample[] | undefined
constructor(options: types.BaseLLMOptions<TInput, TOutput, TModelParams>) {
constructor(
options: SetRequired<
types.BaseLLMOptions<TInput, TOutput, TModelParams>,
'provider' | 'model'
>
) {
super(options)
this._inputSchema = options.inputSchema
this._outputSchema = options.outputSchema
this._provider = options.provider
this._model = options.model
this._modelParams = options.modelParams
@ -48,6 +60,23 @@ export abstract class BaseLLMCallBuilder<
return this as unknown as BaseLLMCallBuilder<TInput, U, TModelParams>
}
public override get inputSchema(): TInput {
if (this._inputSchema) {
return this._inputSchema
} else {
return z.void() as TInput
}
}
public override get outputSchema(): TOutput {
if (this._outputSchema) {
return this._outputSchema
} else {
// TODO: improve typing
return z.string() as unknown as TOutput
}
}
examples(examples: types.LLMExample[]) {
this._examples = examples
return this
@ -56,7 +85,7 @@ export abstract class BaseLLMCallBuilder<
modelParams(params: Partial<TModelParams>) {
// We assume that modelParams does not include nested objects.
// If it did, we would need to do a deep merge.
this._modelParams = Object.assign({}, this._modelParams, params)
this._modelParams = { ...this._modelParams, ...params } as TModelParams
return this
}
@ -75,7 +104,12 @@ export abstract class BaseChatModelBuilder<
> extends BaseLLMCallBuilder<TInput, TOutput, TModelParams> {
_messages: types.ChatMessage[]
constructor(options: types.ChatModelOptions<TInput, TOutput, TModelParams>) {
constructor(
options: SetRequired<
types.ChatModelOptions<TInput, TOutput, TModelParams>,
'provider' | 'model' | 'messages'
>
) {
super(options)
this._messages = options.messages

38
src/openai.test.ts 100644
Wyświetl plik

@ -0,0 +1,38 @@
import test from 'ava'
import { OpenAIClient } from 'openai-fetch'
import { z } from 'zod'
import { OpenAIChatModelBuilder } from './openai'
const client = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! })
test('OpenAIChatModel ⇒ json output', async (t) => {
const builder = new OpenAIChatModelBuilder(client, {
messages: [
{
role: 'user',
content: 'generate fake data'
}
]
}).output(z.object({ foo: z.string(), bar: z.number() }))
const result = await builder.call()
type verify = Expect<
Equal<
typeof result,
{
foo: string
bar: number
}
>
>
})
// Ensure parsed results are typed correctly
// https://github.com/total-typescript/zod-tutorial/blob/main/src/helpers/type-utils.ts
type Expect<T extends true> = T
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
? 1
: 2
? true
: false

Wyświetl plik

@ -1,13 +1,13 @@
import type { SetRequired } from 'type-fest'
import { ZodRawShape, ZodTypeAny, z } from 'zod'
import { type SetRequired } from 'type-fest'
import { ZodTypeAny, z } from 'zod'
import * as types from './types'
import { defaultOpenAIModel } from './constants'
import { BaseChatModelBuilder } from './llm'
export class OpenAIChatModelBuilder<
TInput extends ZodRawShape | ZodTypeAny = ZodTypeAny,
TOutput extends ZodRawShape | ZodTypeAny = z.ZodType<string>
TInput extends ZodTypeAny = ZodTypeAny,
TOutput extends ZodTypeAny = z.ZodType<string>
> extends BaseChatModelBuilder<
TInput,
TOutput,
@ -39,8 +39,8 @@ export class OpenAIChatModelBuilder<
types.BaseChatCompletionResponse<types.openai.ChatCompletionResponse>
> {
return this._client.createChatCompletion({
model: this._model,
...this._modelParams,
model: this._model,
messages
})
}

Wyświetl plik

@ -1,3 +1,4 @@
import { type SetRequired } from 'type-fest'
import { ZodRawShape, ZodTypeAny, z } from 'zod'
import * as types from './types'
@ -15,32 +16,24 @@ export abstract class BaseTaskCallBuilder<
TInput extends ZodRawShape | ZodTypeAny = ZodTypeAny,
TOutput extends ZodRawShape | ZodTypeAny = z.ZodTypeAny
> {
protected _inputSchema: TInput
protected _outputSchema: TOutput
protected _timeoutMs: number
protected _retryConfig: types.RetryConfig
protected _timeoutMs: number | undefined
protected _retryConfig: types.RetryConfig | undefined
constructor(options: types.BaseTaskOptions<TInput, TOutput>) {
this._inputSchema = options.inputSchema
this._outputSchema = options.outputSchema
constructor(options: types.BaseTaskOptions) {
this._timeoutMs = options.timeoutMs
this._retryConfig = options.retryConfig
}
public get inputSchema(): TInput {
return this._inputSchema
}
public abstract get inputSchema(): TInput
public get outputSchema(): TOutput {
return this._outputSchema
}
public abstract get outputSchema(): TOutput
retry(retryConfig: types.RetryConfig) {
public retryConfig(retryConfig: types.RetryConfig) {
this._retryConfig = retryConfig
return this
}
abstract call(
public abstract call(
input?: types.ParsedData<TInput>
): Promise<types.ParsedData<TOutput>>

Wyświetl plik

@ -1,5 +1,6 @@
import { encoding_for_model } from '@dqbd/tiktoken'
import { getEncoding, getEncodingNameForModel } from 'js-tiktoken'
export function getTokenizerForModel(model: string) {
return encoding_for_model(model as any)
const encodingName = getEncodingNameForModel(model as any)
return getEncoding(encodingName)
}

Wyświetl plik

@ -40,18 +40,25 @@ export class MetaphorSearchTool extends BaseTaskCallBuilder<
metaphorClient?: MetaphorClient
} = {}) {
super({
inputSchema: MetaphorSearchToolInputSchema,
outputSchema: MetaphorSearchToolOutputSchema
// TODO
})
this._metaphorClient = metaphorClient
}
public override get inputSchema() {
return MetaphorSearchToolInputSchema
}
public override get outputSchema() {
return MetaphorSearchToolOutputSchema
}
override async call(
input: MetaphorSearchToolInput
): Promise<MetaphorSearchToolOutput> {
// TODO: handle errors gracefully
input = this._inputSchema.parse(input)
input = this.inputSchema.parse(input)
return this._metaphorClient.search({
query: input.query,

Wyświetl plik

@ -24,13 +24,7 @@ export type SafeParsedData<T extends ZodRawShape | ZodTypeAny> =
? SafeParseReturnType<ZodObject<T>, ParsedData<T>>
: never
export interface BaseTaskOptions<
TInput extends ZodRawShape | ZodTypeAny = ZodTypeAny,
TOutput extends ZodRawShape | ZodTypeAny = z.ZodType<string>
> {
inputSchema?: TInput
outputSchema?: TOutput
export interface BaseTaskOptions {
timeoutMs?: number
retryConfig?: RetryConfig
@ -44,7 +38,10 @@ export interface BaseLLMOptions<
TInput extends ZodRawShape | ZodTypeAny = ZodTypeAny,
TOutput extends ZodRawShape | ZodTypeAny = z.ZodType<string>,
TModelParams extends Record<string, any> = Record<string, any>
> extends BaseTaskOptions<TInput, TOutput> {
> extends BaseTaskOptions {
inputSchema?: TInput
outputSchema?: TOutput
provider?: string
model?: string
modelParams?: TModelParams

Wyświetl plik

@ -4,7 +4,8 @@
"lib": ["esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"strict": true,
"noImplicitAny": false,
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"module": "esnext",