kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
pull/643/head^2
rodzic
0953469e8d
commit
0d96cef757
|
@ -4,7 +4,8 @@ import 'dotenv/config'
|
||||||
import { gracefulExit } from 'exit-hook'
|
import { gracefulExit } from 'exit-hook'
|
||||||
import restoreCursor from 'restore-cursor'
|
import restoreCursor from 'restore-cursor'
|
||||||
|
|
||||||
import type * as types from '@/types.js'
|
// import { ClearbitClient } from '../src/index.js'
|
||||||
|
import { ProxycurlClient } from '../src/services/proxycurl-client.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scratch for quick testing.
|
* Scratch for quick testing.
|
||||||
|
@ -12,6 +13,19 @@ import type * as types from '@/types.js'
|
||||||
async function main() {
|
async function main() {
|
||||||
restoreCursor()
|
restoreCursor()
|
||||||
|
|
||||||
|
// const clearbit = new ClearbitClient()
|
||||||
|
// const res = await clearbit.companyEnrichment({
|
||||||
|
// domain: 'https://clay.com'
|
||||||
|
// })
|
||||||
|
// console.log(JSON.stringify(res, null, 2))
|
||||||
|
|
||||||
|
const proxycurl = new ProxycurlClient()
|
||||||
|
const res = await proxycurl.getLinkedInPerson({
|
||||||
|
linkedin_profile_url: 'https://linkedin.com/in/fisch2'
|
||||||
|
// personal_email: 'fisch0920@gmail.com'
|
||||||
|
})
|
||||||
|
console.log(JSON.stringify(res, null, 2))
|
||||||
|
|
||||||
return gracefulExit(0)
|
return gracefulExit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
14
package.json
14
package.json
|
@ -45,10 +45,10 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dexaai/dexter": "^2.0.0",
|
"@dexaai/dexter": "^2.0.0",
|
||||||
|
"@nangohq/node": "^0.39.30",
|
||||||
"chalk": "^5.3.0",
|
"chalk": "^5.3.0",
|
||||||
"delay": "^6.0.0",
|
"delay": "^6.0.0",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"execa": "^8.0.1",
|
|
||||||
"exit-hook": "^4.0.0",
|
"exit-hook": "^4.0.0",
|
||||||
"jsonrepair": "^3.6.1",
|
"jsonrepair": "^3.6.1",
|
||||||
"ky": "^1.2.4",
|
"ky": "^1.2.4",
|
||||||
|
@ -56,10 +56,14 @@
|
||||||
"p-map": "^7.0.2",
|
"p-map": "^7.0.2",
|
||||||
"p-retry": "^6.2.0",
|
"p-retry": "^6.2.0",
|
||||||
"p-throttle": "^6.1.0",
|
"p-throttle": "^6.1.0",
|
||||||
|
"proxycurl-js-linkedin-profile-scraper": "^1.0.2",
|
||||||
|
"reflect-metadata": "^0.2.2",
|
||||||
"restore-cursor": "^5.0.0",
|
"restore-cursor": "^5.0.0",
|
||||||
"tiny-invariant": "^1.3.3",
|
"tiny-invariant": "^1.3.3",
|
||||||
|
"twitter-api-sdk": "^1.2.1",
|
||||||
"type-fest": "^4.16.0",
|
"type-fest": "^4.16.0",
|
||||||
"zod": "^3.23.3"
|
"zod": "^3.23.3",
|
||||||
|
"zod-to-json-schema": "^3.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fisch0920/eslint-config": "^1.3.1",
|
"@fisch0920/eslint-config": "^1.3.1",
|
||||||
|
@ -68,12 +72,12 @@
|
||||||
"del-cli": "^5.1.0",
|
"del-cli": "^5.1.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"husky": "^9.0.11",
|
"husky": "^9.0.11",
|
||||||
"lint-staged": "^15.2.2",
|
"lint-staged": "^15.2.4",
|
||||||
"np": "^10.0.5",
|
"np": "^10.0.5",
|
||||||
"npm-run-all2": "^6.1.2",
|
"npm-run-all2": "^6.2.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"tsup": "^8.0.2",
|
"tsup": "^8.0.2",
|
||||||
"tsx": "^4.7.2",
|
"tsx": "^4.10.5",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.4.5",
|
||||||
"vite": "^5.2.10",
|
"vite": "^5.2.10",
|
||||||
"vitest": "^1.5.0"
|
"vitest": "^1.5.0"
|
||||||
|
|
556
pnpm-lock.yaml
556
pnpm-lock.yaml
Plik diff jest za duży
Load Diff
File diff suppressed because one or more lines are too long
Plik diff jest za duży
Load Diff
106
src/fns.ts
106
src/fns.ts
|
@ -9,13 +9,6 @@ import { zodToJsonSchema } from './zod-to-json-schema.js'
|
||||||
|
|
||||||
export const invocableMetadataKey = Symbol('invocable')
|
export const invocableMetadataKey = Symbol('invocable')
|
||||||
|
|
||||||
export interface Invocable {
|
|
||||||
name: string
|
|
||||||
description?: string
|
|
||||||
inputSchema?: z.AnyZodObject
|
|
||||||
callback: (args: Record<string, any>) => Promise<any>
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class AIToolsProvider {
|
export abstract class AIToolsProvider {
|
||||||
private _tools?: ToolSet
|
private _tools?: ToolSet
|
||||||
private _functions?: FunctionSet
|
private _functions?: FunctionSet
|
||||||
|
@ -34,7 +27,16 @@ export abstract class AIToolsProvider {
|
||||||
|
|
||||||
get functions(): FunctionSet {
|
get functions(): FunctionSet {
|
||||||
if (!this._functions) {
|
if (!this._functions) {
|
||||||
const invocables = getInvocables(this)
|
const metadata = this.constructor[Symbol.metadata]
|
||||||
|
const invocables = (metadata?.invocables as Invocable[]) ?? []
|
||||||
|
const namespace = this.namespace
|
||||||
|
|
||||||
|
const functions = invocables.map((invocable) => ({
|
||||||
|
...invocable,
|
||||||
|
name: invocable.name ?? `${namespace}_${invocable.propertyKey}`,
|
||||||
|
callback: (target as any)[invocable.propertyKey].bind(target)
|
||||||
|
}))
|
||||||
|
|
||||||
const functions = invocables.map(getFunctionSpec)
|
const functions = invocables.map(getFunctionSpec)
|
||||||
this._functions = new FunctionSet(functions)
|
this._functions = new FunctionSet(functions)
|
||||||
}
|
}
|
||||||
|
@ -43,7 +45,14 @@ export abstract class AIToolsProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFunctionSpec(invocable: Invocable): types.AIFunctionSpec {
|
export interface Invocable {
|
||||||
|
name: string
|
||||||
|
description?: string
|
||||||
|
inputSchema?: z.AnyZodObject
|
||||||
|
callback: (args: Record<string, any>) => Promise<any>
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFunctionSpec(invocable: Invocable): types.AIFunctionSpec {
|
||||||
const { name, description, inputSchema } = invocable
|
const { name, description, inputSchema } = invocable
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -58,14 +67,11 @@ export function getFunctionSpec(invocable: Invocable): types.AIFunctionSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function aiFunction<
|
||||||
* Constraints:
|
This,
|
||||||
* - params must be an object, so the underlying function should only expect a
|
Args extends any[],
|
||||||
* single parameter
|
Return extends Promise<any>
|
||||||
* - for the return value type `T | MaybePromise<T>`, `T` must be serializable
|
>({
|
||||||
* to JSON
|
|
||||||
*/
|
|
||||||
export function aiFunction({
|
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
inputSchema
|
inputSchema
|
||||||
|
@ -77,48 +83,38 @@ export function aiFunction({
|
||||||
// single parameter
|
// single parameter
|
||||||
inputSchema?: z.AnyZodObject
|
inputSchema?: z.AnyZodObject
|
||||||
}) {
|
}) {
|
||||||
return function (
|
return (
|
||||||
target: object,
|
targetMethod: (this: This, ...args: Args) => Return,
|
||||||
propertyKey: string,
|
context: ClassMethodDecoratorContext<
|
||||||
descriptor: PropertyDescriptor
|
This,
|
||||||
) {
|
(this: This, ...args: Args) => Return
|
||||||
const existingInvocables = getPrivateInvocables(target)
|
> & {
|
||||||
|
readonly metadata: {
|
||||||
|
invocables: Invocable[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const methodName = String(context.name)
|
||||||
|
if (!context.metadata.invocables) {
|
||||||
|
context.metadata.invocables = []
|
||||||
|
}
|
||||||
|
|
||||||
existingInvocables.push({
|
context.metadata.invocables.push({
|
||||||
propertyKey,
|
name: name ?? methodName,
|
||||||
description,
|
description,
|
||||||
name,
|
inputSchema,
|
||||||
inputSchema
|
callback: targetMethod
|
||||||
})
|
})
|
||||||
|
|
||||||
setPrivateInvocables(target, existingInvocables)
|
return targetMethod
|
||||||
|
|
||||||
return descriptor.get ?? descriptor.value
|
// function replacementMethod(this: This, ...args: Args): Return {
|
||||||
|
// console.log(`LOG: Entering method '${methodName}'.`)
|
||||||
|
// const result = targetMethod.call(this, ...args)
|
||||||
|
// console.log(`LOG: Exiting method '${methodName}'.`)
|
||||||
|
// return result
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return replacementMethod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getInvocables(target: object): Invocable[] {
|
|
||||||
const invocables = getPrivateInvocables(target)
|
|
||||||
const namespace = target.constructor.name
|
|
||||||
|
|
||||||
return invocables.map((invocable) => ({
|
|
||||||
...invocable,
|
|
||||||
name: invocable.name ?? `${namespace}_${invocable.propertyKey}`,
|
|
||||||
callback: (target as any)[invocable.propertyKey].bind(target)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PrivateInvocable {
|
|
||||||
propertyKey: string
|
|
||||||
name?: string
|
|
||||||
description?: string
|
|
||||||
inputSchema?: z.AnyZodObject
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPrivateInvocables(target: object): PrivateInvocable[] {
|
|
||||||
return Reflect.getMetadata(invocableMetadataKey, target) ?? []
|
|
||||||
}
|
|
||||||
|
|
||||||
function setPrivateInvocables(target: object, invocables: PrivateInvocable[]) {
|
|
||||||
Reflect.defineMetadata(invocableMetadataKey, invocables, target)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
export * from './fns.js'
|
|
||||||
export * from './function-set.js'
|
export * from './function-set.js'
|
||||||
export * from './parse-structured-output.js'
|
export * from './parse-structured-output.js'
|
||||||
export * from './services/index.js'
|
export * from './services/index.js'
|
||||||
|
|
|
@ -24,7 +24,7 @@ export class DexaClient {
|
||||||
this.ky = ky.extend({ prefixUrl: this.apiBaseUrl, timeout: 60_000 })
|
this.ky = ky.extend({ prefixUrl: this.apiBaseUrl, timeout: 60_000 })
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateResponse({ messages }: { messages: Prompt.Msg[] }) {
|
async askDexa({ messages }: { messages: Prompt.Msg[] }) {
|
||||||
return this.ky
|
return this.ky
|
||||||
.post('api/ask-dexa', {
|
.post('api/ask-dexa', {
|
||||||
json: {
|
json: {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
export * from './clearbit.js'
|
export * from './clearbit-client.js'
|
||||||
export * from './dexa-client.js'
|
export * from './dexa-client.js'
|
||||||
export * from './diffbot.js'
|
export * from './diffbot-client.js'
|
||||||
export * from './openai-client.js'
|
export * from './openai-client.js'
|
||||||
|
export * from './proxycurl-client.js'
|
||||||
export * from './scraper-client.js'
|
export * from './scraper-client.js'
|
||||||
export * from './serpapi.js'
|
export * from './serpapi-client.js'
|
||||||
export * from './serper.js'
|
export * from './serper-client.js'
|
||||||
export * from './twitter-client.js'
|
export * from './twitter-client.js'
|
||||||
export * from './weather.js'
|
export * from './weather-client.js'
|
||||||
|
|
Plik diff jest za duży
Load Diff
|
@ -2,6 +2,7 @@ import defaultKy, { type KyInstance } from 'ky'
|
||||||
|
|
||||||
import { assert, getEnv } from '../utils.js'
|
import { assert, getEnv } from '../utils.js'
|
||||||
|
|
||||||
|
export namespace scraper {
|
||||||
export type ScrapeResult = {
|
export type ScrapeResult = {
|
||||||
author: string
|
author: string
|
||||||
byline: string
|
byline: string
|
||||||
|
@ -22,6 +23,7 @@ export type ScrapeResult = {
|
||||||
textContent: string
|
textContent: string
|
||||||
title: string
|
title: string
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a single endpoint API for scraping websites. It returns the HTML,
|
* This is a single endpoint API for scraping websites. It returns the HTML,
|
||||||
|
@ -56,7 +58,7 @@ export class ScraperClient {
|
||||||
}: {
|
}: {
|
||||||
timeout?: number
|
timeout?: number
|
||||||
} = {}
|
} = {}
|
||||||
): Promise<ScrapeResult> {
|
): Promise<scraper.ScrapeResult> {
|
||||||
return this.ky
|
return this.ky
|
||||||
.post('scrape', {
|
.post('scrape', {
|
||||||
json: { url },
|
json: { url },
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// https://github.com/microsoft/TypeScript/issues/53461
|
||||||
|
// symbol-polyfill.ts
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface SymbolConstructor {
|
||||||
|
readonly metadata: unique symbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
;(Symbol as any).metadata ??= Symbol.for('Symbol.metadata')
|
||||||
|
|
||||||
|
const _metadata = Object.create(null)
|
||||||
|
|
||||||
|
if (typeof Symbol === 'function' && Symbol.metadata) {
|
||||||
|
Object.defineProperty(globalThis, Symbol.metadata, {
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
writable: true,
|
||||||
|
value: _metadata
|
||||||
|
})
|
||||||
|
}
|
102
src/types.ts
102
src/types.ts
|
@ -14,3 +14,105 @@ export interface AIToolSpec {
|
||||||
type: 'function'
|
type: 'function'
|
||||||
function: AIFunctionSpec
|
function: AIFunctionSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic/default OpenAI message without any narrowing applied
|
||||||
|
*/
|
||||||
|
export interface Msg {
|
||||||
|
/** The contents of the message. `content` is required for all messages, and may be null for assistant messages with function calls. */
|
||||||
|
content: string | null
|
||||||
|
/** The role of the messages author. One of `system`, `user`, `assistant`, 'tool', or `function`. */
|
||||||
|
role: Msg.Role
|
||||||
|
/** The name and arguments of a function that should be called, as generated by the model. */
|
||||||
|
function_call?: Msg.Call.Function
|
||||||
|
/** The tool calls generated by the model, such as function calls. */
|
||||||
|
tool_calls?: Msg.Call.Tool[]
|
||||||
|
/**
|
||||||
|
* Tool call that this message is responding to.
|
||||||
|
*/
|
||||||
|
tool_call_id?: string
|
||||||
|
/**
|
||||||
|
* The name of the author of this message. `name` is required if role is
|
||||||
|
* `function`, and it should be the name of the function whose response is in the
|
||||||
|
* `content`. May contain a-z, A-Z, 0-9, and underscores, with a maximum length of
|
||||||
|
* 64 characters.
|
||||||
|
*/
|
||||||
|
name?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Narrowed ChatModel.Message types. */
|
||||||
|
export namespace Msg {
|
||||||
|
/** The possible roles for a message. */
|
||||||
|
export type Role = 'system' | 'user' | 'assistant' | 'function' | 'tool'
|
||||||
|
|
||||||
|
export namespace Call {
|
||||||
|
/** The name and arguments of a function that should be called, as generated by the model. */
|
||||||
|
export type Function = {
|
||||||
|
/** The arguments to call the function with, as generated by the model in JSON format. */
|
||||||
|
arguments: string
|
||||||
|
/** The name of the function to call. */
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The tool calls generated by the model, such as function calls. */
|
||||||
|
export type Tool = {
|
||||||
|
/** The ID of the tool call. */
|
||||||
|
id: string
|
||||||
|
/** The type of the tool. Currently, only `function` is supported. */
|
||||||
|
type: 'function'
|
||||||
|
/** The function that the model called. */
|
||||||
|
function: Call.Function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Message with text content for the system. */
|
||||||
|
export type System = {
|
||||||
|
role: 'system'
|
||||||
|
content: string
|
||||||
|
name?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Message with text content from the user. */
|
||||||
|
export type User = {
|
||||||
|
role: 'user'
|
||||||
|
name?: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Message with text content from the assistant. */
|
||||||
|
export type Assistant = {
|
||||||
|
role: 'assistant'
|
||||||
|
name?: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Message with arguments to call a function. */
|
||||||
|
export type FuncCall = {
|
||||||
|
role: 'assistant'
|
||||||
|
name?: string
|
||||||
|
content: null
|
||||||
|
function_call: Call.Function
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Message with the result of a function call. */
|
||||||
|
export type FuncResult = {
|
||||||
|
role: 'function'
|
||||||
|
name: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Message with arguments to call one or more tools. */
|
||||||
|
export type ToolCall = {
|
||||||
|
role: 'assistant'
|
||||||
|
name?: string
|
||||||
|
content: null
|
||||||
|
tool_calls: Call.Tool[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Message with the result of a tool call. */
|
||||||
|
export type ToolResult = {
|
||||||
|
role: 'tool'
|
||||||
|
tool_call_id: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ export function zodToJsonSchema(schema: z.ZodType): Record<string, unknown> {
|
||||||
'default',
|
'default',
|
||||||
'definitions',
|
'definitions',
|
||||||
'description',
|
'description',
|
||||||
'markdownDescription',
|
'markdownDescription'
|
||||||
'additionalProperties'
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue