kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
pull/700/head
rodzic
f353dd55b3
commit
fdedede6a1
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
|
@ -19,6 +19,12 @@
|
|||
|
||||
**See the [github repo](https://github.com/transitive-bullshit/agentic) or [docs](https://agentic.so) for more info.**
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] Convert HTML in descriptions to markdown.
|
||||
- [ ] Properly format multiline function comments.
|
||||
- [ ] Debug stripe schema issue.
|
||||
|
||||
## License
|
||||
|
||||
MIT © [Travis Fischer](https://x.com/transitive_bs)
|
||||
|
|
|
@ -6,7 +6,6 @@ import { fileURLToPath } from 'node:url'
|
|||
import type { IJsonSchema, OpenAPIV3 } from 'openapi-types'
|
||||
import { assert } from '@agentic/core'
|
||||
import SwaggerParser from '@apidevtools/swagger-parser'
|
||||
import camelCase from 'camelcase'
|
||||
import decamelize from 'decamelize'
|
||||
import { execa } from 'execa'
|
||||
|
||||
|
@ -15,11 +14,13 @@ import {
|
|||
dereference,
|
||||
dereferenceFull,
|
||||
getAndResolve,
|
||||
getComponentDisplayName,
|
||||
getComponentName,
|
||||
getOperationParamsName,
|
||||
getOperationResponseName,
|
||||
jsonSchemaToZod,
|
||||
naiveMergeJSONSchemas,
|
||||
pascalCase,
|
||||
prettify
|
||||
} from './utils'
|
||||
|
||||
|
@ -62,7 +63,7 @@ async function main() {
|
|||
openapiSpecName.toLowerCase() === openapiSpecName,
|
||||
`OpenAPI spec name "${openapiSpecName}" must be in kebab case`
|
||||
)
|
||||
const name = camelCase(openapiSpecName, { pascalCase: true })
|
||||
const name = pascalCase(openapiSpecName)
|
||||
const nameLowerCase = name.toLowerCase()
|
||||
const nameSnakeCase = decamelize(name)
|
||||
const nameKebabCase = decamelize(name, { separator: '-' })
|
||||
|
@ -214,7 +215,7 @@ async function main() {
|
|||
operationParamsSources[key] = source
|
||||
}
|
||||
} else if (derefed?.anyOf || derefed?.oneOf) {
|
||||
const componentName = getComponentName(schema.$ref)
|
||||
const componentName = getComponentDisplayName(schema.$ref)
|
||||
operationParamsSources[componentName] = source
|
||||
|
||||
// TODO: handle this case
|
||||
|
@ -368,7 +369,7 @@ async function main() {
|
|||
|
||||
if (operationParamsJSONSchema.$refs.length) {
|
||||
const refSchemas = operationParamsJSONSchema.$refs.map(
|
||||
(ref) => `${getComponentName(ref)!}Schema`
|
||||
(ref) => `${getComponentDisplayName(ref)!}Schema`
|
||||
)
|
||||
|
||||
operationsParamsSchema = operationsParamsSchema.replace(
|
||||
|
@ -394,7 +395,7 @@ async function main() {
|
|||
let isDuplicateDefinition = false
|
||||
|
||||
if (operationResponseJSONSchema.$ref) {
|
||||
const componentName = getComponentName(
|
||||
const componentName = getComponentDisplayName(
|
||||
operationResponseJSONSchema.$ref
|
||||
)
|
||||
if (componentName === operationResponseName) {
|
||||
|
@ -475,12 +476,13 @@ async function main() {
|
|||
if (description && !/[!.?]$/.test(description)) {
|
||||
description += '.'
|
||||
}
|
||||
const isDescriptionMultiline = description?.includes('\n')
|
||||
|
||||
const aiClientMethod = `
|
||||
${description ? `/**\n * ${description}\n */` : ''}
|
||||
@aiFunction({
|
||||
name: '${operationNameSnakeCase}',
|
||||
${description ? `description: '${description}',` : ''}${hasUnionParams ? '\n// TODO: Improve handling of union params' : ''}
|
||||
${description ? `description: ${isDescriptionMultiline ? `\`${description.replaceAll('`', '\\`')}\`` : `'${description}'`},` : ''}${hasUnionParams ? '\n// TODO: Improve handling of union params' : ''}
|
||||
inputSchema: ${namespaceName}.${operationParamsName}Schema${hasUnionParams ? ' as any' : ''},
|
||||
})
|
||||
async ${operationName}(${!hasParams ? '_' : ''}params: ${namespaceName}.${operationParamsName}): Promise<${namespaceName}.${operationResponseName}> {
|
||||
|
@ -527,7 +529,7 @@ async function main() {
|
|||
)
|
||||
|
||||
for (const ref of sortedComponents) {
|
||||
const type = getComponentName(ref)
|
||||
const type = getComponentDisplayName(ref)
|
||||
assert(type, `Invalid ref name ${ref}`)
|
||||
|
||||
const name = `${type}Schema`
|
||||
|
@ -539,17 +541,17 @@ async function main() {
|
|||
|
||||
processedComponents.add(ref)
|
||||
|
||||
if (type === 'SearchResponse') {
|
||||
console.log(type, dereferenced)
|
||||
}
|
||||
|
||||
const schema = jsonSchemaToZod(dereferenced, { name, type })
|
||||
componentSchemas[type] = schema
|
||||
}
|
||||
|
||||
console.log(
|
||||
'\ncomponents',
|
||||
Array.from(sortedComponents).map((ref) => getComponentName(ref))
|
||||
JSON.stringify(
|
||||
sortedComponents.map((ref) => getComponentName(ref)),
|
||||
null,
|
||||
2
|
||||
)
|
||||
)
|
||||
|
||||
// console.log(
|
||||
|
@ -585,33 +587,32 @@ import defaultKy, { type KyInstance } from 'ky'
|
|||
import { z } from 'zod'`.trim()
|
||||
|
||||
const commentLine = `// ${'-'.repeat(77)}`
|
||||
const outputTypes = (
|
||||
const outputTypes = [
|
||||
header,
|
||||
`export namespace ${namespaceName} {`,
|
||||
apiBaseUrl ? `export const apiBaseUrl = '${apiBaseUrl}'` : undefined,
|
||||
Object.values(componentSchemas).length
|
||||
? `${commentLine}\n// Component schemas\n${commentLine}`
|
||||
: undefined,
|
||||
...Object.values(componentSchemas),
|
||||
Object.values(operationSchemas).length
|
||||
? `${commentLine}\n// Operation schemas\n${commentLine}`
|
||||
: undefined,
|
||||
...Object.values(operationSchemas),
|
||||
'}'
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('\n\n')
|
||||
|
||||
const output = (
|
||||
await prettify(
|
||||
[
|
||||
header,
|
||||
`export namespace ${namespaceName} {`,
|
||||
apiBaseUrl ? `export const apiBaseUrl = '${apiBaseUrl}'` : undefined,
|
||||
Object.values(componentSchemas).length
|
||||
? `${commentLine}\n// Component schemas\n${commentLine}`
|
||||
: undefined,
|
||||
...Object.values(componentSchemas),
|
||||
Object.values(operationSchemas).length
|
||||
? `${commentLine}\n// Operation schemas\n${commentLine}`
|
||||
: undefined,
|
||||
...Object.values(operationSchemas),
|
||||
'}'
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('\n\n')
|
||||
)
|
||||
)
|
||||
.replaceAll(/z\s*\.object\({}\)\s*\.merge\(([^)]*)\)/gm, '$1')
|
||||
.replaceAll(/\/\*\*(\S.*)\*\//g, '/** $1 */')
|
||||
outputTypes,
|
||||
`
|
||||
|
||||
const output = await prettify(
|
||||
[
|
||||
outputTypes,
|
||||
`
|
||||
/**
|
||||
* Agentic client for ${name}.${spec.info?.description ? `\n * ${spec.info.description}` : ''}
|
||||
*/
|
||||
export class ${clientName} extends AIFunctionsProvider {
|
||||
protected readonly ky: KyInstance
|
||||
${hasGlobalApiKeyInHeader ? 'protected readonly apiKey: string' : ''}
|
||||
|
@ -651,12 +652,15 @@ export class ${clientName} extends AIFunctionsProvider {
|
|||
})
|
||||
}
|
||||
`,
|
||||
aiClientMethodsString,
|
||||
'}'
|
||||
].join('\n\n')
|
||||
aiClientMethodsString,
|
||||
'}'
|
||||
].join('\n\n')
|
||||
)
|
||||
)
|
||||
.replaceAll(/z\s*\.object\({}\)\s*\.merge\(([^)]*)\)/gm, '$1')
|
||||
.replaceAll(/\/\*\*(\S.*)\*\//g, '/** $1 */')
|
||||
|
||||
// console.log(output)
|
||||
console.log(output)
|
||||
await fs.mkdir(destFolder, { recursive: true })
|
||||
await fs.writeFile(destFileClient, output)
|
||||
await execa('npx', ['eslint', '--fix', destFileClient])
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type SwaggerParser from '@apidevtools/swagger-parser'
|
||||
import type { IJsonSchema, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'
|
||||
import { assert } from '@agentic/core'
|
||||
import camelcase from 'camelcase'
|
||||
import {
|
||||
type JsonSchema,
|
||||
jsonSchemaToZod as jsonSchemaToZodImpl,
|
||||
|
@ -21,8 +22,8 @@ export function prettify(source: string): Promise<string> {
|
|||
})
|
||||
}
|
||||
|
||||
export function titleCase(identifier: string): string {
|
||||
return `${identifier.slice(0, 1).toUpperCase()}${identifier.slice(1)}`
|
||||
export function pascalCase(identifier: string): string {
|
||||
return camelcase(identifier, { pascalCase: true })
|
||||
}
|
||||
|
||||
export function unTitleCase(identifier: string): string {
|
||||
|
@ -240,10 +241,15 @@ const reservedWords = new Set([
|
|||
])
|
||||
|
||||
export function getComponentName(ref: string) {
|
||||
const name0 = ref.split('/').pop()!
|
||||
assert(name0, `Invalid ref name ${ref}`)
|
||||
const name = ref.split('/').pop()!
|
||||
assert(name, `Invalid ref name ${ref}`)
|
||||
|
||||
const name1 = titleCase(name0)
|
||||
return name
|
||||
}
|
||||
|
||||
export function getComponentDisplayName(ref: string) {
|
||||
const name0 = getComponentName(ref)
|
||||
const name1 = pascalCase(name0)
|
||||
assert(name1, `Invalid ref name ${ref}`)
|
||||
|
||||
if (reservedWords.has(name1)) {
|
||||
|
@ -257,7 +263,7 @@ export function getOperationParamsName(
|
|||
operationName: string,
|
||||
schemas?: Record<string, string>
|
||||
) {
|
||||
const name = `${titleCase(operationName)}Params`
|
||||
const name = `${pascalCase(operationName)}Params`
|
||||
if (!schemas) return name
|
||||
|
||||
let tempName = name
|
||||
|
@ -274,7 +280,7 @@ export function getOperationResponseName(
|
|||
operationName: string,
|
||||
schemas?: Record<string, string>
|
||||
) {
|
||||
const name = `${titleCase(operationName)}Response`
|
||||
const name = `${pascalCase(operationName)}Response`
|
||||
if (!schemas) return name
|
||||
|
||||
let tempName = name
|
||||
|
|
Ładowanie…
Reference in New Issue