kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: WIP core typed llm fluent interface
rodzic
427b2783af
commit
a2b20ad8b5
|
@ -0,0 +1,8 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
# This is an example .env file.
|
||||
#
|
||||
# All of these environment vars must be defined either in your environment or in
|
||||
# a local .env file in order to run the demo for this project.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
OPENAI_API_KEY=
|
10
package.json
10
package.json
|
@ -54,5 +54,13 @@
|
|||
"openapi",
|
||||
"guardrails",
|
||||
"plugins"
|
||||
]
|
||||
],
|
||||
"dependencies": {
|
||||
"dotenv-safe": "^8.2.0",
|
||||
"openai-fetch": "^1.2.0",
|
||||
"p-map": "^6.0.0",
|
||||
"parse-json": "^7.0.0",
|
||||
"zod": "^3.21.4",
|
||||
"zod-validation-error": "^1.3.0"
|
||||
}
|
||||
}
|
||||
|
|
Plik diff jest za duży
Load Diff
37
readme.md
37
readme.md
|
@ -15,6 +15,43 @@
|
|||
|
||||
TODO
|
||||
|
||||
## Use Cases
|
||||
|
||||
https://platform.openai.com/examples
|
||||
|
||||
- text completion
|
||||
- text generation
|
||||
- text classification
|
||||
- https://platform.openai.com/docs/guides/completion/classification
|
||||
- https://docs.cohere.com/docs/text-classification-with-classify
|
||||
- special cases
|
||||
- content moderation
|
||||
- https://platform.openai.com/docs/guides/moderation/overview
|
||||
- language detection
|
||||
- conversation
|
||||
- transformation
|
||||
- translation
|
||||
- conversion
|
||||
- summarization
|
||||
- completion
|
||||
- factual responses
|
||||
- chat completion
|
||||
- entity extraction
|
||||
- reranking
|
||||
|
||||
// take in a query and a list of texts and produces an ordered array with each text assigned a relevance score.
|
||||
// generate JSON conforming to a zod schema
|
||||
// generate a string conforming to a zod schema
|
||||
// generate TS code and ensure it is valid syntax + valid exports
|
||||
// generate HTML and ensure it parses correctly
|
||||
// primitives (boolean, number, z.coerce.date, string)
|
||||
// classifier (enum)
|
||||
// CSV
|
||||
|
||||
// retry strategies
|
||||
|
||||
// separate the prompt formatting from the inference call?
|
||||
|
||||
## License
|
||||
|
||||
MIT © [Travis Fischer](https://transitivebullsh.it)
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import { z } from 'zod'
|
||||
|
||||
async function main() {
|
||||
const $ = {} as any
|
||||
|
||||
// work with a single input or an array of inputs using p-map under the hood
|
||||
|
||||
const ex0 = await $.gpt4(`give me a single boolean value: `)
|
||||
.output(z.boolean())
|
||||
.retry({ attempts: 3 })
|
||||
.call()
|
||||
|
||||
// LLM
|
||||
// give me a single boolean value
|
||||
// given an output as a boolean.
|
||||
// true/false
|
||||
|
||||
const ex1 = await $.gpt4(
|
||||
'give me a list of character names from star wars'
|
||||
).output(z.array(z.string().nonempty()))
|
||||
|
||||
const ex2 = await $.gpt4(`Summarize the following text: {{text}}`)
|
||||
.output(z.string().nonempty())
|
||||
.input(z.object({ text: z.string().nonempty() }))
|
||||
.call({
|
||||
text: 'The quick brown fox jumps over the lazy dog.'
|
||||
})
|
||||
|
||||
const ext22 = await $.gpt4({ temperature: 0 }).call({
|
||||
messages: [
|
||||
// TEST
|
||||
]
|
||||
})
|
||||
|
||||
const ex3 = await $.gpt4({
|
||||
temperature: 0,
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: 'You extract movie titles from text.'
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `Extract the movie title from the following text or return 'none' if no movie title is found.`
|
||||
}
|
||||
]
|
||||
})
|
||||
.examples([
|
||||
{
|
||||
input: `Deadpool 2 | Official HD Deadpool's "Wet on Wet" Teaser | 2018`,
|
||||
output: `Deadpool 2`
|
||||
},
|
||||
{
|
||||
input: `Jordan Peele Just Became the First Black Writer-Director With a $100M Movie Debut`,
|
||||
output: 'none'
|
||||
},
|
||||
{
|
||||
input: 'Joker Officially Rated “R”',
|
||||
output: 'Joker'
|
||||
},
|
||||
{
|
||||
input: `Ryan Reynolds’ 'Free Guy' Receives July 3, 2020 Release Date - About a bank teller stuck in his routine that discovers he’s an NPC character in a brutal open world game.`,
|
||||
output: 'Free Guy'
|
||||
},
|
||||
{
|
||||
input: 'James Cameron congratulates Kevin Feige and Marvel!',
|
||||
output: 'none'
|
||||
},
|
||||
{
|
||||
input:
|
||||
'The Cast of Guardians of the Galaxy release statement on James Gunn',
|
||||
output: 'Guardians of the Galaxy'
|
||||
}
|
||||
])
|
||||
.output(z.string().nonempty())
|
||||
.call()
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './utils'
|
|
@ -0,0 +1,139 @@
|
|||
import type { ZodType } from 'zod'
|
||||
|
||||
import * as types from './types'
|
||||
|
||||
export class Agentic {
|
||||
_client: types.openai.OpenAIClient
|
||||
_verbosity: number
|
||||
_defaults: Pick<
|
||||
types.BaseLLMOptions,
|
||||
'provider' | 'model' | 'modelParams' | 'timeoutMs' | 'retryConfig'
|
||||
>
|
||||
|
||||
constructor(
|
||||
client: types.openai.OpenAIClient,
|
||||
opts: {
|
||||
verbosity?: number
|
||||
defaults?: Pick<
|
||||
types.BaseLLMOptions,
|
||||
'provider' | 'model' | 'modelParams' | 'timeoutMs' | 'retryConfig'
|
||||
>
|
||||
} = {}
|
||||
) {
|
||||
this._client = client
|
||||
this._verbosity = opts.verbosity ?? 0
|
||||
this._defaults = {
|
||||
provider: 'openai',
|
||||
model: 'gpt-3.5-turbo',
|
||||
modelParams: {},
|
||||
timeoutMs: 30000,
|
||||
retryConfig: {
|
||||
attempts: 3,
|
||||
strategy: 'heal',
|
||||
...opts.defaults?.retryConfig
|
||||
},
|
||||
...opts.defaults
|
||||
}
|
||||
}
|
||||
|
||||
gpt4(
|
||||
promptOrChatCompletionParams: string | types.openai.ChatCompletionParams
|
||||
) {
|
||||
let options: Omit<types.openai.ChatCompletionParams, 'model'>
|
||||
|
||||
if (typeof promptOrChatCompletionParams === 'string') {
|
||||
options = {
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: promptOrChatCompletionParams
|
||||
}
|
||||
]
|
||||
}
|
||||
} else {
|
||||
options = promptOrChatCompletionParams
|
||||
|
||||
if (!options.messages) {
|
||||
throw new Error()
|
||||
}
|
||||
}
|
||||
|
||||
return new OpenAIChatModelBuilder(this._client, {
|
||||
...(this._defaults as any), // TODO
|
||||
model: 'gpt-4',
|
||||
...options
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class BaseLLMCallBuilder<TInput, TOutput, TModelParams> {
|
||||
_options: types.BaseLLMOptions<TInput, TOutput, TModelParams>
|
||||
|
||||
constructor(options: types.BaseLLMOptions<TInput, TOutput, TModelParams>) {
|
||||
this._options = options
|
||||
}
|
||||
|
||||
input(inputSchema: ZodType<TInput>) {
|
||||
this._options.input = inputSchema
|
||||
return this
|
||||
}
|
||||
|
||||
output(outputSchema: ZodType<TOutput>) {
|
||||
this._options.output = outputSchema
|
||||
return this
|
||||
}
|
||||
|
||||
examples(examples: types.LLMExample[]) {
|
||||
this._options.examples = examples
|
||||
return this
|
||||
}
|
||||
|
||||
retry(retryConfig: types.LLMRetryConfig) {
|
||||
this._options.retryConfig = retryConfig
|
||||
return this
|
||||
}
|
||||
|
||||
abstract call(input?: TInput): Promise<TOutput>
|
||||
}
|
||||
|
||||
export abstract class ChatModelBuilder<
|
||||
TInput,
|
||||
TOutput,
|
||||
TModelParams
|
||||
> extends BaseLLMCallBuilder<TInput, TOutput, TModelParams> {
|
||||
_messages: types.ChatMessage[]
|
||||
|
||||
constructor(options: types.ChatModelOptions<TInput, TOutput, TModelParams>) {
|
||||
super(options)
|
||||
|
||||
this._messages = options.messages
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenAIChatModelBuilder<TInput, TOutput> extends ChatModelBuilder<
|
||||
TInput,
|
||||
TOutput,
|
||||
Omit<types.openai.ChatCompletionParams, 'messages'>
|
||||
> {
|
||||
_client: types.openai.OpenAIClient
|
||||
|
||||
constructor(
|
||||
client: types.openai.OpenAIClient,
|
||||
options: types.ChatModelOptions<
|
||||
TInput,
|
||||
TOutput,
|
||||
Omit<types.openai.ChatCompletionParams, 'messages'>
|
||||
>
|
||||
) {
|
||||
super({
|
||||
provider: 'openai',
|
||||
...options
|
||||
})
|
||||
|
||||
this._client = client
|
||||
}
|
||||
|
||||
override async call(input?: TInput): Promise<TOutput> {
|
||||
// TODO
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import * as openai from 'openai-fetch'
|
||||
import type { ZodType } from 'zod'
|
||||
|
||||
export { openai }
|
||||
|
||||
export interface BaseLLMOptions<
|
||||
TInput = any,
|
||||
TOutput = any,
|
||||
TModelParams = Record<string, any>
|
||||
> {
|
||||
provider?: string
|
||||
model?: string
|
||||
modelParams?: TModelParams
|
||||
timeoutMs?: number
|
||||
|
||||
input?: ZodType<TInput>
|
||||
output?: ZodType<TOutput>
|
||||
examples?: LLMExample[]
|
||||
retryConfig?: LLMRetryConfig
|
||||
}
|
||||
|
||||
export interface LLMOptions<
|
||||
TInput = any,
|
||||
TOutput = any,
|
||||
TModelParams = Record<string, any>
|
||||
> extends BaseLLMOptions<TInput, TOutput, TModelParams> {
|
||||
promptTemplate?: string
|
||||
promptPrefix?: string
|
||||
promptSuffix?: string
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
role: 'user' | 'system' | 'assistant' | 'tool'
|
||||
content: string
|
||||
}
|
||||
|
||||
export interface ChatModelOptions<
|
||||
TInput = any,
|
||||
TOutput = any,
|
||||
TModelParams = Record<string, any>
|
||||
> extends BaseLLMOptions<TInput, TOutput, TModelParams> {
|
||||
messages: ChatMessage[]
|
||||
}
|
||||
|
||||
export interface LLMExample {
|
||||
input: string
|
||||
output: string
|
||||
}
|
||||
|
||||
export interface LLMRetryConfig {
|
||||
attempts: number
|
||||
strategy: string
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
import dotenv from 'dotenv-safe'
|
||||
import { OpenAIClient } from 'openai-fetch'
|
||||
import { z } from 'zod'
|
||||
import { fromZodError } from 'zod-validation-error'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
interface Temp {
|
||||
contentType: string
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY })
|
||||
|
||||
const outputSchema = z.object({})
|
||||
|
||||
const res = await openai.createChatCompletion({
|
||||
model: 'gpt-4',
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: ''
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const out = await infer('give me a single boolean value', z.boolean(), {})
|
||||
}
|
||||
|
||||
async function infer<T = any>(
|
||||
prompt: string,
|
||||
schema: z.ZodType<T>,
|
||||
{ retry }
|
||||
): Promise<T> {}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error('error', err)
|
||||
process.exit(1)
|
||||
})
|
Ładowanie…
Reference in New Issue