kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: WIP added tools to mcp and openapi origin adapters
rodzic
b07599972e
commit
8ed18982ad
|
|
@ -26,6 +26,7 @@
|
|||
"test:typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentic/platform": "workspace:*",
|
||||
"@agentic/platform-core": "workspace:*",
|
||||
"@agentic/platform-schemas": "workspace:*",
|
||||
"@agentic/platform-validators": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { validateAgenticProjectConfig } from '@agentic/platform'
|
||||
import { assert, parseZodSchema, sha256 } from '@agentic/platform-core'
|
||||
import { validateAgenticProjectConfig } from '@agentic/platform-schemas'
|
||||
import { validators } from '@agentic/platform-validators'
|
||||
import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
"test:typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentic/platform": "workspace:*",
|
||||
"@agentic/platform-api-client": "workspace:*",
|
||||
"@agentic/platform-core": "workspace:*",
|
||||
"@agentic/platform-schemas": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
"test:unit": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentic/platform": "workspace:*",
|
||||
"@agentic/platform-api-client": "workspace:*",
|
||||
"@agentic/platform-core": "workspace:*",
|
||||
"@agentic/platform-schemas": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import {
|
||||
type AgenticProjectConfig,
|
||||
validateAgenticProjectConfig
|
||||
} from '@agentic/platform-schemas'
|
||||
import type { AgenticProjectConfig } from '@agentic/platform-schemas'
|
||||
import { validateAgenticProjectConfig } from '@agentic/platform'
|
||||
import { loadConfig } from 'unconfig'
|
||||
|
||||
export async function loadAgenticConfig({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'Test Invalid Name 0',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'Test-Invalid-Name-1',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test_invalid_name_2',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
originUrl: 'https://jsonplaceholder.typicode.com'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: '@foo/bar', // invalid; name contains invalid characters
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-invalid-origin-url-0',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-invalid-origin-url-1',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-invalid-origin-url-3',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-pricing-base-inconsistent',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-pricing-custom-inconsistent',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-pricing-duplicate-0',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-pricing-duplicate-1',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-pricing-empty-0',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-pricing-empty-1',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-pricing-empty-2',
|
||||
|
|
|
|||
|
|
@ -25,6 +25,6 @@
|
|||
"test:typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentic/platform-schemas": "workspace:*"
|
||||
"@agentic/platform": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-basic-raw-free-ts',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-pricing-3-plans',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-pricing-custom-0',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defaultFreePricingPlan, defineConfig } from '@agentic/platform-schemas'
|
||||
import { defaultFreePricingPlan, defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
// TODO: resolve name / slug conflicts
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-pricing-monthly-annual',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { defineConfig } from '@agentic/platform-schemas'
|
||||
import { defineConfig } from '@agentic/platform'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'test-pricing-pay-as-you-go',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"openapi": "3.0.2",
|
||||
"info": {
|
||||
"title": "OpenAPI Mixed Test Fixture",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/echo/{id}": {
|
||||
"parameters": [
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "id",
|
||||
"type": "string"
|
||||
},
|
||||
"name": "id",
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"summary": "Echo",
|
||||
"operationId": "echo",
|
||||
"parameters": [
|
||||
{
|
||||
"required": false,
|
||||
"schema": {
|
||||
"title": "x-custom-header",
|
||||
"type": "string"
|
||||
},
|
||||
"name": "x-custom-header",
|
||||
"in": "header"
|
||||
},
|
||||
{
|
||||
"required": false,
|
||||
"schema": {
|
||||
"title": "name",
|
||||
"type": "string"
|
||||
},
|
||||
"name": "name",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://test-openapi-basic.now.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -24,7 +24,10 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@agentic/platform-core": "workspace:*",
|
||||
"@redocly/openapi-core": "catalog:"
|
||||
"@agentic/platform-schemas": "workspace:*",
|
||||
"@redocly/openapi-core": "catalog:",
|
||||
"camelcase": "^8.0.0",
|
||||
"decamelize": "^6.0.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
|||
Plik diff jest za duży
Load Diff
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
exports[`validateOpenAPISpec > basic.json (file url) 1`] = `
|
||||
{
|
||||
"components": {},
|
||||
"info": {
|
||||
"title": "OpenAPI Basic Test Fixture",
|
||||
"version": "0.1.0",
|
||||
|
|
@ -47,7 +46,6 @@ exports[`validateOpenAPISpec > basic.json (file url) 1`] = `
|
|||
|
||||
exports[`validateOpenAPISpec > basic.json (http url) 1`] = `
|
||||
{
|
||||
"components": {},
|
||||
"info": {
|
||||
"title": "OpenAPI Basic Test Fixture",
|
||||
"version": "0.1.0",
|
||||
|
|
@ -92,7 +90,6 @@ exports[`validateOpenAPISpec > basic.json (http url) 1`] = `
|
|||
|
||||
exports[`validateOpenAPISpec > basic.json (string) 1`] = `
|
||||
{
|
||||
"components": {},
|
||||
"info": {
|
||||
"title": "OpenAPI Basic Test Fixture",
|
||||
"version": "0.1.0",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
import { readFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import { describe, expect, test } from 'vitest'
|
||||
|
||||
import { getToolsFromOpenAPISpec } from './get-tools-from-openapi-spec'
|
||||
import { validateOpenAPISpec } from './validate-openapi-spec'
|
||||
|
||||
const validFixtures = [
|
||||
'basic.json',
|
||||
'mixed.json',
|
||||
'firecrawl.json',
|
||||
'open-meteo.yaml',
|
||||
'pet-store.json',
|
||||
'petstore-expanded.json',
|
||||
'security.json',
|
||||
'tic-tac-toe.json'
|
||||
]
|
||||
|
||||
const invalidFixtures = ['notion.json']
|
||||
|
||||
const fixturesDir = path.join(
|
||||
fileURLToPath(import.meta.url),
|
||||
'..',
|
||||
'..',
|
||||
'fixtures'
|
||||
)
|
||||
|
||||
describe('getToolsFromOpenAPISpec', () => {
|
||||
for (const fixture of validFixtures) {
|
||||
test(
|
||||
fixture,
|
||||
{
|
||||
timeout: 60_000
|
||||
},
|
||||
async () => {
|
||||
const fixturePath = path.join(fixturesDir, fixture)
|
||||
const source = await readFile(fixturePath, 'utf8')
|
||||
|
||||
const spec = await validateOpenAPISpec(source, {
|
||||
dereference: true
|
||||
})
|
||||
const result = await getToolsFromOpenAPISpec(spec)
|
||||
|
||||
expect(result).toMatchSnapshot()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
for (const fixture of invalidFixtures) {
|
||||
test(
|
||||
`${fixture} (invalid)`,
|
||||
{
|
||||
timeout: 60_000
|
||||
},
|
||||
async () => {
|
||||
const fixturePath = path.join(fixturesDir, fixture)
|
||||
const source = await readFile(fixturePath, 'utf8')
|
||||
|
||||
const spec = await validateOpenAPISpec(source, {
|
||||
dereference: true
|
||||
})
|
||||
|
||||
await expect(async () =>
|
||||
getToolsFromOpenAPISpec(spec)
|
||||
).rejects.toThrowError()
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
import { assert, parseZodSchema } from '@agentic/platform-core'
|
||||
import {
|
||||
type OpenAPIOperationHttpMethod,
|
||||
type OpenAPIOperationParameterSource,
|
||||
type OpenAPIToolOperation,
|
||||
openapiToolOperationSchema,
|
||||
type Tool,
|
||||
toolSchema
|
||||
} from '@agentic/platform-schemas'
|
||||
import decamelize from 'decamelize'
|
||||
|
||||
import type {
|
||||
DereferencedLooseOpenAPI3Spec,
|
||||
OperationObject,
|
||||
ParameterObject,
|
||||
SchemaObject
|
||||
} from './types'
|
||||
import { convertParametersToJsonSchema } from './openapi-parameters-to-json-schema'
|
||||
import { camelCase, mergeJsonSchemaObjects } from './utils'
|
||||
|
||||
const jsonContentType = 'application/json'
|
||||
const multipartFormData = 'multipart/form-data'
|
||||
|
||||
const httpMethods = ['get', 'post', 'put', 'delete', 'patch', 'trace'] as const
|
||||
const paramSources = ['body', 'formData', 'header', 'path', 'query'] as const
|
||||
|
||||
/**
|
||||
* Converts a fully dereferenced and validated OpenAPI spec into an array of
|
||||
* MCP-compatible tools, with a map of tool names to their corresponding OpenAPI
|
||||
* operations.
|
||||
*
|
||||
* This allows us to expose OpenAPI HTTP operations as MCP tools and convert
|
||||
* MCP tool calls to corresponding HTTP requests.
|
||||
*/
|
||||
export async function getToolsFromOpenAPISpec(
|
||||
spec: DereferencedLooseOpenAPI3Spec
|
||||
): Promise<{
|
||||
tools: Tool[]
|
||||
toolToOperationMap: Record<string, OpenAPIToolOperation>
|
||||
}> {
|
||||
const tools: Tool[] = []
|
||||
const toolToOperationMap: Record<string, OpenAPIToolOperation> = {}
|
||||
|
||||
const requestBodyJsonSchemaPaths = [
|
||||
'requestBody',
|
||||
'content',
|
||||
jsonContentType,
|
||||
'schema'
|
||||
]
|
||||
|
||||
const requestBodyFormDataJsonSchemaPaths = [
|
||||
'requestBody',
|
||||
'content',
|
||||
multipartFormData,
|
||||
'schema'
|
||||
]
|
||||
|
||||
const operationResponsePaths = [
|
||||
['responses', '200', 'content', jsonContentType, 'schema'],
|
||||
['responses', '201', 'content', jsonContentType, 'schema']
|
||||
// ['responses', 'default', 'content', jsonContentType, 'schema']
|
||||
]
|
||||
|
||||
const operationRequestPaths = [
|
||||
requestBodyJsonSchemaPaths,
|
||||
requestBodyFormDataJsonSchemaPaths
|
||||
]
|
||||
|
||||
const operationNames = new Set<string>()
|
||||
|
||||
for (const path in spec.paths) {
|
||||
const pathItem = spec.paths[path]
|
||||
assert(pathItem)
|
||||
// console.log(JSON.stringify(pathItem, null, 2))
|
||||
|
||||
const pathParamsJsonSchema = {
|
||||
type: 'object',
|
||||
properties: {} as Record<string, SchemaObject>,
|
||||
required: [] as string[]
|
||||
} satisfies SchemaObject
|
||||
const pathParamsSources: Record<string, OpenAPIOperationParameterSource> =
|
||||
{}
|
||||
|
||||
if (pathItem.parameters) {
|
||||
const params = convertParametersToJsonSchema(
|
||||
pathItem.parameters as ParameterObject[]
|
||||
)
|
||||
|
||||
for (const source of paramSources) {
|
||||
if (params[source]) {
|
||||
mergeJsonSchemaObjects(pathParamsJsonSchema, params[source], {
|
||||
source,
|
||||
sources: pathParamsSources,
|
||||
label: `path "${path}"`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const method of httpMethods) {
|
||||
const operation = pathItem[method] as OperationObject
|
||||
if (!operation) {
|
||||
continue
|
||||
}
|
||||
|
||||
const operationId =
|
||||
operation.operationId || `${method}${path.replaceAll(/\W+/g, '_')}`
|
||||
assert(
|
||||
operationId,
|
||||
`Invalid operation id "${operationId}" for OpenAPI path "${method} ${path}"`
|
||||
)
|
||||
|
||||
const operationName = camelCase(operationId.replaceAll('/', '_'))
|
||||
const operationNameSnakeCase = decamelize(operationName)
|
||||
assert(
|
||||
!operationNames.has(operationName),
|
||||
`Duplicate operation name "${operationName}"`
|
||||
)
|
||||
operationNames.add(operationName)
|
||||
|
||||
const operationParamsJsonSchema = structuredClone(pathParamsJsonSchema)
|
||||
const operationParamsSources: Record<
|
||||
string,
|
||||
OpenAPIOperationParameterSource
|
||||
> = structuredClone(pathParamsSources)
|
||||
const operationResponseJsonSchemas: Record<string, SchemaObject> = {}
|
||||
|
||||
for (const schemaPath of operationRequestPaths) {
|
||||
let current: any = operation
|
||||
for (const key of schemaPath) {
|
||||
current = current[key]
|
||||
if (!current) break
|
||||
}
|
||||
|
||||
if (current) {
|
||||
mergeJsonSchemaObjects(operationParamsJsonSchema, current, {
|
||||
source: schemaPath[2] === jsonContentType ? 'body' : 'formData',
|
||||
sources: operationParamsSources,
|
||||
label: `operation "${operationId}"`
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for (const schemaPath of operationResponsePaths) {
|
||||
let current: any = operation
|
||||
for (const key of schemaPath) {
|
||||
current = current[key]
|
||||
if (!current) break
|
||||
}
|
||||
|
||||
if (current) {
|
||||
const status = schemaPath[1]!
|
||||
assert(
|
||||
status,
|
||||
`Invalid status ${status} for operation ${operationName}`
|
||||
)
|
||||
|
||||
if (current.type !== 'object') {
|
||||
// console.warn(
|
||||
// `Invalid OpenAPI response type "${current.type}" for operation "${operationName}"`
|
||||
// )
|
||||
break
|
||||
}
|
||||
|
||||
operationResponseJsonSchemas[status] = current
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (operation.parameters) {
|
||||
const params = convertParametersToJsonSchema(
|
||||
operation.parameters as ParameterObject[]
|
||||
)
|
||||
|
||||
for (const source of paramSources) {
|
||||
if (params[source]) {
|
||||
mergeJsonSchemaObjects(pathParamsJsonSchema, params[source], {
|
||||
source,
|
||||
sources: operationParamsSources,
|
||||
label: `operation "${operationId}"`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const operationResponseJsonSchema =
|
||||
operationResponseJsonSchemas['200'] ||
|
||||
operationResponseJsonSchemas['201']
|
||||
const description = operation.description || operation.summary
|
||||
const { tags } = operation
|
||||
|
||||
tools.push(
|
||||
parseZodSchema(toolSchema, {
|
||||
name: operationNameSnakeCase,
|
||||
description,
|
||||
inputSchema: operationParamsJsonSchema,
|
||||
outputSchema: operationResponseJsonSchema
|
||||
})
|
||||
)
|
||||
|
||||
toolToOperationMap[operationNameSnakeCase] = parseZodSchema(
|
||||
openapiToolOperationSchema,
|
||||
{
|
||||
operationId,
|
||||
method: method.toLowerCase() as OpenAPIOperationHttpMethod,
|
||||
path,
|
||||
parameterSources: operationParamsSources,
|
||||
tags
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tools,
|
||||
toolToOperationMap
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
export * from './get-tools-from-openapi-spec'
|
||||
export * from './redocly-config'
|
||||
export type * from './types'
|
||||
export * from './validate-openapi-spec'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,224 @@
|
|||
/**
|
||||
* This file is forked from: https://github.com/kogosoftwarellc/open-api/tree/main/packages/openapi-jsonschema-parameters
|
||||
*
|
||||
* Several fixes have been applied.
|
||||
*
|
||||
* The original code is licensed under the MIT license.
|
||||
*/
|
||||
|
||||
import type { ParameterObject, SchemaObject } from './types'
|
||||
|
||||
export interface OpenAPIParametersAsJsonSchema {
|
||||
body?: SchemaObject
|
||||
formData?: SchemaObject
|
||||
header?: SchemaObject
|
||||
path?: SchemaObject
|
||||
query?: SchemaObject
|
||||
cookie?: SchemaObject
|
||||
}
|
||||
|
||||
const VALIDATION_KEYWORDS = new Set([
|
||||
'additionalItems',
|
||||
'default',
|
||||
'example',
|
||||
'description',
|
||||
'enum',
|
||||
'examples',
|
||||
'exclusiveMaximum',
|
||||
'exclusiveMinimum',
|
||||
'format',
|
||||
'items',
|
||||
'maxItems',
|
||||
'maxLength',
|
||||
'maximum',
|
||||
'minItems',
|
||||
'minLength',
|
||||
'minimum',
|
||||
'multipleOf',
|
||||
'pattern',
|
||||
'title',
|
||||
'type',
|
||||
'uniqueItems'
|
||||
])
|
||||
|
||||
const SUBSCHEMA_KEYWORDS = [
|
||||
'additionalItems',
|
||||
'items',
|
||||
'contains',
|
||||
'additionalProperties',
|
||||
'propertyNames',
|
||||
'not'
|
||||
]
|
||||
|
||||
const SUBSCHEMA_ARRAY_KEYWORDS = ['items', 'allOf', 'anyOf', 'oneOf']
|
||||
|
||||
const SUBSCHEMA_OBJECT_KEYWORDS = [
|
||||
'definitions',
|
||||
'properties',
|
||||
'patternProperties',
|
||||
'dependencies'
|
||||
]
|
||||
|
||||
export function convertParametersToJsonSchema(
|
||||
parameters: ParameterObject[]
|
||||
): OpenAPIParametersAsJsonSchema {
|
||||
const parametersSchema: OpenAPIParametersAsJsonSchema = {}
|
||||
const bodySchema = getBodySchema(parameters)
|
||||
const formDataSchema = getSchema(parameters, 'formData')
|
||||
const headerSchema = getSchema(parameters, 'header')
|
||||
const pathSchema = getSchema(parameters, 'path')
|
||||
const querySchema = getSchema(parameters, 'query')
|
||||
const cookieSchema = getSchema(parameters, 'cookie')
|
||||
|
||||
if (bodySchema) {
|
||||
parametersSchema.body = bodySchema
|
||||
}
|
||||
|
||||
if (formDataSchema) {
|
||||
parametersSchema.formData = formDataSchema
|
||||
}
|
||||
|
||||
if (headerSchema) {
|
||||
parametersSchema.header = headerSchema
|
||||
}
|
||||
|
||||
if (pathSchema) {
|
||||
parametersSchema.path = pathSchema
|
||||
}
|
||||
|
||||
if (querySchema) {
|
||||
parametersSchema.query = querySchema
|
||||
}
|
||||
|
||||
if (cookieSchema) {
|
||||
parametersSchema.cookie = cookieSchema
|
||||
}
|
||||
|
||||
return parametersSchema
|
||||
}
|
||||
|
||||
function copyValidationKeywords(src: any) {
|
||||
const dst: any = {}
|
||||
for (let i = 0, keys = Object.keys(src), len = keys.length; i < len; i++) {
|
||||
const keyword = keys[i]
|
||||
if (!keyword) continue
|
||||
|
||||
if (VALIDATION_KEYWORDS.has(keyword) || keyword.slice(0, 2) === 'x-') {
|
||||
dst[keyword] = src[keyword]
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
function handleNullable(schema: SchemaObject) {
|
||||
return { anyOf: [schema, { type: 'null' }] }
|
||||
}
|
||||
|
||||
function handleNullableSchema(schema: any) {
|
||||
if (typeof schema !== 'object' || schema === null) {
|
||||
return schema
|
||||
}
|
||||
|
||||
const newSchema = { ...schema }
|
||||
|
||||
for (const keyword of SUBSCHEMA_KEYWORDS) {
|
||||
if (
|
||||
typeof schema[keyword] === 'object' &&
|
||||
schema[keyword] !== null &&
|
||||
!Array.isArray(schema[keyword])
|
||||
) {
|
||||
newSchema[keyword] = handleNullableSchema(schema[keyword])
|
||||
}
|
||||
}
|
||||
|
||||
for (const keyword of SUBSCHEMA_ARRAY_KEYWORDS) {
|
||||
if (Array.isArray(schema[keyword])) {
|
||||
newSchema[keyword] = schema[keyword].map(handleNullableSchema)
|
||||
}
|
||||
}
|
||||
|
||||
for (const keyword of SUBSCHEMA_OBJECT_KEYWORDS) {
|
||||
if (typeof schema[keyword] === 'object' && schema[keyword] !== null) {
|
||||
newSchema[keyword] = { ...schema[keyword] }
|
||||
for (const prop of Object.keys(schema[keyword])) {
|
||||
newSchema[keyword][prop] = handleNullableSchema(schema[keyword][prop])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete newSchema.$ref
|
||||
|
||||
if (schema.nullable) {
|
||||
delete newSchema.nullable
|
||||
return handleNullable(newSchema)
|
||||
}
|
||||
|
||||
return newSchema
|
||||
}
|
||||
|
||||
function getBodySchema(parameters: any[]) {
|
||||
let bodySchema = parameters.find((param) => {
|
||||
return param.in === 'body' && param.schema
|
||||
})
|
||||
|
||||
if (bodySchema) {
|
||||
bodySchema = bodySchema.schema
|
||||
}
|
||||
|
||||
return bodySchema
|
||||
}
|
||||
|
||||
function getSchema(parameters: any[], type: string) {
|
||||
const params = parameters.filter(byIn(type))
|
||||
let schema: any
|
||||
|
||||
if (params.length) {
|
||||
schema = { type: 'object', properties: {} }
|
||||
|
||||
for (const param of params) {
|
||||
let paramSchema = copyValidationKeywords(param)
|
||||
|
||||
if ('schema' in param) {
|
||||
paramSchema = {
|
||||
...paramSchema,
|
||||
...handleNullableSchema(param.schema)
|
||||
}
|
||||
if ('examples' in param) {
|
||||
paramSchema.examples = getExamples(param.examples)
|
||||
}
|
||||
schema.properties[param.name] = paramSchema
|
||||
} else {
|
||||
if ('examples' in paramSchema) {
|
||||
paramSchema.examples = getExamples(paramSchema.examples)
|
||||
}
|
||||
schema.properties[param.name] = param.nullable
|
||||
? handleNullable(paramSchema)
|
||||
: paramSchema
|
||||
}
|
||||
}
|
||||
|
||||
schema.required = getRequiredParams(params)
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
function getRequiredParams(parameters: any[]) {
|
||||
return parameters.filter(byRequired).map(toName)
|
||||
}
|
||||
|
||||
function getExamples(exampleSchema: any) {
|
||||
return Object.keys(exampleSchema).map((k) => exampleSchema[k].value)
|
||||
}
|
||||
|
||||
function byIn(str: string) {
|
||||
return (param: any) => param.in === str && param.type !== 'file'
|
||||
}
|
||||
|
||||
function byRequired(param: any) {
|
||||
return !!param.required
|
||||
}
|
||||
|
||||
function toName(param: any) {
|
||||
return param.name
|
||||
}
|
||||
|
|
@ -624,3 +624,19 @@ export type SecurityRequirementObject = {
|
|||
}
|
||||
|
||||
export type $defs = Record<string, SchemaObject>
|
||||
|
||||
type Deref<T> =
|
||||
// 1. Remove any direct ReferenceObject
|
||||
T extends ReferenceObject
|
||||
? never
|
||||
: // 2. Recurse into arrays/tuples
|
||||
T extends readonly (infer U)[]
|
||||
? readonly Deref<U>[]
|
||||
: // 3. Recurse into object properties
|
||||
T extends object
|
||||
? { [K in keyof T]: Deref<T[K]> }
|
||||
: // 4. Primitives, functions, etc. stay unchanged
|
||||
T
|
||||
|
||||
/** LooseOpenAPI3Spec with every `$ref` wiped out */
|
||||
export type DereferencedLooseOpenAPI3Spec = Deref<LooseOpenAPI3Spec>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
import type { OpenAPIOperationParameterSource } from '@agentic/platform-schemas'
|
||||
import { assert } from '@agentic/platform-core'
|
||||
import camelCaseImpl from 'camelcase'
|
||||
|
||||
import type { ObjectSubtype, SchemaObject } from './types'
|
||||
|
||||
export function camelCase(identifier: string): string {
|
||||
return camelCaseImpl(identifier)
|
||||
}
|
||||
|
||||
export function mergeJsonSchemaObjects(
|
||||
schema0: SchemaObject & ObjectSubtype,
|
||||
schema1: SchemaObject,
|
||||
{
|
||||
source,
|
||||
sources,
|
||||
label
|
||||
}: {
|
||||
source: OpenAPIOperationParameterSource
|
||||
sources: Record<string, OpenAPIOperationParameterSource>
|
||||
label: string
|
||||
}
|
||||
) {
|
||||
if (schema1.type === 'object' && schema1.properties) {
|
||||
schema0.properties = {
|
||||
...schema0.properties,
|
||||
...schema1.properties
|
||||
}
|
||||
|
||||
for (const key of Object.keys(schema1.properties)) {
|
||||
assert(
|
||||
!sources[key],
|
||||
`Duplicate parameter "${key}" in OpenAPI spec ${label}`
|
||||
)
|
||||
|
||||
sources[key] = source
|
||||
}
|
||||
}
|
||||
|
||||
if (schema1.required) {
|
||||
schema0.required = Array.from(
|
||||
new Set([...(schema0.required || []), ...schema1.required])
|
||||
)
|
||||
}
|
||||
|
||||
// https://community.openai.com/t/official-documentation-for-supported-schemas-for-response-format-parameter-in-calls-to-client-beta-chats-completions-parse/932422/3
|
||||
// https://platform.openai.com/docs/guides/structured-outputs
|
||||
// https://json-schema.org/understanding-json-schema/reference/combining
|
||||
assert(
|
||||
!schema1.oneOf,
|
||||
`JSON schema "oneOf" is not supported in OpenAPI spec ${label}`
|
||||
)
|
||||
assert(
|
||||
!schema1.allOf,
|
||||
`JSON schema "oneOf" is not supported in OpenAPI spec ${label}`
|
||||
)
|
||||
// TODO: Support "anyOf" which should be supported by OpenAI function calling
|
||||
assert(
|
||||
!schema1.anyOf,
|
||||
`JSON schema "anyOf" is not supported in OpenAPI spec ${label}`
|
||||
)
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ import {
|
|||
Source
|
||||
} from '@redocly/openapi-core'
|
||||
|
||||
import type { LooseOpenAPI3Spec } from './types'
|
||||
import type { DereferencedLooseOpenAPI3Spec, LooseOpenAPI3Spec } from './types'
|
||||
import { getDefaultRedoclyConfig } from './redocly-config'
|
||||
|
||||
interface ParseSchemaOptions {
|
||||
|
|
@ -27,20 +27,26 @@ interface ParseSchemaOptions {
|
|||
*
|
||||
* Adapted from https://github.com/openapi-ts/openapi-typescript/blob/main/packages/openapi-typescript/src/lib/redoc.ts
|
||||
*/
|
||||
export async function validateOpenAPISpec(
|
||||
export async function validateOpenAPISpec<
|
||||
TDereference extends boolean | undefined
|
||||
>(
|
||||
source: string | URL | Buffer | Record<string, unknown>,
|
||||
{
|
||||
cwd,
|
||||
redoclyConfig,
|
||||
logger = console,
|
||||
silent = false
|
||||
logger,
|
||||
silent = false,
|
||||
dereference = false
|
||||
}: {
|
||||
cwd?: URL
|
||||
redoclyConfig?: RedoclyConfig
|
||||
logger?: Logger
|
||||
silent?: boolean
|
||||
dereference?: TDereference
|
||||
} = {}
|
||||
): Promise<LooseOpenAPI3Spec> {
|
||||
): Promise<
|
||||
TDereference extends true ? DereferencedLooseOpenAPI3Spec : LooseOpenAPI3Spec
|
||||
> {
|
||||
if (!redoclyConfig) {
|
||||
redoclyConfig = await getDefaultRedoclyConfig()
|
||||
}
|
||||
|
|
@ -95,9 +101,11 @@ export async function validateOpenAPISpec(
|
|||
_processProblems(problems, { silent, logger })
|
||||
|
||||
const bundled = await bundle({
|
||||
doc: document,
|
||||
config: redoclyConfig,
|
||||
dereference: false,
|
||||
doc: document
|
||||
dereference,
|
||||
removeUnusedComponents: true,
|
||||
externalRefResolver: resolver
|
||||
})
|
||||
_processProblems(bundled.problems, { silent, logger })
|
||||
|
||||
|
|
@ -195,7 +203,7 @@ function _processProblems(
|
|||
logger,
|
||||
silent
|
||||
}: {
|
||||
logger: Logger
|
||||
logger?: Logger
|
||||
silent: boolean
|
||||
}
|
||||
) {
|
||||
|
|
@ -210,9 +218,9 @@ function _processProblems(
|
|||
|
||||
if (problem.severity === 'error') {
|
||||
errorMessage = problemMessage
|
||||
logger.error('openapi spec error', problemMessage)
|
||||
logger?.error('openapi spec error', problemMessage)
|
||||
} else if (!silent) {
|
||||
logger.warn('openapi spec warning', problemMessage)
|
||||
logger?.warn('openapi spec warning', problemMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "@agentic/platform",
|
||||
"version": "0.0.1",
|
||||
"description": "Public SDK for developers building on top of the Agentic platform.",
|
||||
"author": "Travis Fischer <travis@transitivebullsh.it>",
|
||||
"license": "UNLICENSED",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/transitive-bullshit/agentic-platform.git",
|
||||
"directory": "packages/platform"
|
||||
},
|
||||
"type": "module",
|
||||
"source": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "run-s test:*",
|
||||
"test:lint": "eslint .",
|
||||
"test:typecheck": "tsc --noEmit",
|
||||
"test:unit": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentic/platform-core": "workspace:*",
|
||||
"@agentic/platform-openapi": "workspace:*",
|
||||
"@agentic/platform-schemas": "workspace:*",
|
||||
"@agentic/platform-validators": "workspace:*",
|
||||
"@hono/zod-openapi": "catalog:",
|
||||
"@modelcontextprotocol/sdk": "catalog:",
|
||||
"semver": "catalog:",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/semver": "catalog:",
|
||||
"restore-cursor": "catalog:",
|
||||
"zod-to-json-schema": "catalog:"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
import { parseZodSchema } from '@agentic/platform-core'
|
||||
|
||||
import {
|
||||
type AgenticProjectConfig,
|
||||
type AgenticProjectConfigInput,
|
||||
agenticProjectConfigSchema
|
||||
} from './agentic-project-config'
|
||||
} from '@agentic/platform-schemas'
|
||||
|
||||
/**
|
||||
* This method allows Agentic projects to define their configs in a type-safe
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export * from './define-config'
|
||||
export * from './validate-agentic-project-config'
|
||||
export * from './validate-origin-adapter'
|
||||
export * from './validate-tools'
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
import type { ZodTypeDef } from 'zod'
|
||||
import { assert, type Logger, parseZodSchema } from '@agentic/platform-core'
|
||||
import { validators } from '@agentic/platform-validators'
|
||||
import { clean as cleanSemver, valid as isValidSemver } from 'semver'
|
||||
|
||||
import type { PricingPlanLineItem } from './pricing'
|
||||
import {
|
||||
type AgenticProjectConfig,
|
||||
type AgenticProjectConfigInput,
|
||||
agenticProjectConfigSchema,
|
||||
getPricingPlansByInterval,
|
||||
type PricingPlanLineItem,
|
||||
type ResolvedAgenticProjectConfig,
|
||||
resolvedAgenticProjectConfigSchema
|
||||
} from './agentic-project-config'
|
||||
import { getPricingPlansByInterval } from './utils'
|
||||
} from '@agentic/platform-schemas'
|
||||
import { validators } from '@agentic/platform-validators'
|
||||
import { clean as cleanSemver, valid as isValidSemver } from 'semver'
|
||||
|
||||
import { validateOriginAdapter } from './validate-origin-adapter'
|
||||
|
||||
export async function validateAgenticProjectConfig(
|
||||
|
|
@ -4,7 +4,10 @@ import type {
|
|||
Tool
|
||||
} from '@agentic/platform-schemas'
|
||||
import { assert, type Logger } from '@agentic/platform-core'
|
||||
import { validateOpenAPISpec } from '@agentic/platform-openapi'
|
||||
import {
|
||||
getToolsFromOpenAPISpec,
|
||||
validateOpenAPISpec
|
||||
} from '@agentic/platform-openapi'
|
||||
import { Client as McpClient } from '@modelcontextprotocol/sdk/client/index.js'
|
||||
|
||||
/**
|
||||
|
|
@ -57,11 +60,25 @@ export async function validateOriginAdapter({
|
|||
|
||||
// TODO: Extract tool definitions from OpenAPI operationIds
|
||||
|
||||
const dereferencedOpenAPISpec = await validateOpenAPISpec(
|
||||
originAdapter.spec,
|
||||
{
|
||||
cwd,
|
||||
dereference: true
|
||||
}
|
||||
)
|
||||
|
||||
const { tools, toolToOperationMap } = await getToolsFromOpenAPISpec(
|
||||
dereferencedOpenAPISpec
|
||||
)
|
||||
|
||||
return {
|
||||
tools,
|
||||
originAdapter: {
|
||||
...originAdapter,
|
||||
// Update the openapi spec with the normalized version
|
||||
spec: JSON.stringify(openapiSpec)
|
||||
spec: JSON.stringify(openapiSpec),
|
||||
toolToOperationMap
|
||||
}
|
||||
}
|
||||
} else if (originAdapter.type === 'mcp') {
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import type { OriginAdapter, Tool, ToolConfig } from '@agentic/platform-schemas'
|
||||
import { assert } from '@agentic/platform-core'
|
||||
|
||||
/**
|
||||
* Validates and normalizes the origin adapter config for a project.
|
||||
*/
|
||||
export async function validateTools({
|
||||
originAdapter,
|
||||
tools,
|
||||
toolConfigs,
|
||||
label
|
||||
}: {
|
||||
originAdapter: OriginAdapter
|
||||
tools: Tool[]
|
||||
toolConfigs: ToolConfig[]
|
||||
label: string
|
||||
}): Promise<void> {
|
||||
assert(tools.length > 0, 400, `No tools defined for ${label}`)
|
||||
|
||||
const toolsMap: Record<string, Tool> = {}
|
||||
for (const tool of tools) {
|
||||
assert(
|
||||
!toolsMap[tool.name],
|
||||
400,
|
||||
`Duplicate tool name "${tool.name}" found in ${label}`
|
||||
)
|
||||
toolsMap[tool.name] = tool
|
||||
}
|
||||
|
||||
for (const toolConfig of toolConfigs) {
|
||||
const tool = toolsMap[toolConfig.name]
|
||||
assert(
|
||||
tool,
|
||||
400,
|
||||
`Tool "${toolConfig.name}" from \`toolConfigs\` not found in \`tools\` for ${label}`
|
||||
)
|
||||
}
|
||||
|
||||
if (originAdapter.type === 'openapi') {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"extends": "@fisch0920/config/tsconfig-node",
|
||||
"include": ["src", "*.config.ts", "bin/*", "../schemas/src/utils.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
@ -23,18 +23,12 @@
|
|||
"test:unit": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentic/platform-core": "workspace:*",
|
||||
"@agentic/platform-openapi": "workspace:*",
|
||||
"@agentic/platform-validators": "workspace:*",
|
||||
"@hono/zod-openapi": "catalog:",
|
||||
"@modelcontextprotocol/sdk": "catalog:",
|
||||
"ms": "catalog:",
|
||||
"semver": "catalog:",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ms": "catalog:",
|
||||
"@types/semver": "catalog:",
|
||||
"restore-cursor": "catalog:",
|
||||
"zod-to-json-schema": "catalog:"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
export * from './agentic-project-config'
|
||||
export * from './define-config'
|
||||
export * from './mcp'
|
||||
export * from './origin-adapter'
|
||||
export * from './pricing'
|
||||
|
|
@ -7,6 +6,4 @@ export * from './rate-limit'
|
|||
export * from './subjects'
|
||||
export * from './tools'
|
||||
export * from './utils'
|
||||
export * from './validate-agentic-project-config'
|
||||
export * from './validate-origin-adapter'
|
||||
export * from './webhook'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { z } from '@hono/zod-openapi'
|
||||
|
||||
import { mcpServerInfoSchema } from './mcp'
|
||||
import { toolNameSchema } from './tools'
|
||||
|
||||
export const originAdapterLocationSchema = z.literal('external')
|
||||
// z.union([
|
||||
|
|
@ -103,6 +104,43 @@ NOTE: Agentic currently only supports \`external\` API servers. If you'd like to
|
|||
.openapi('OriginAdapterConfig')
|
||||
export type OriginAdapterConfig = z.infer<typeof originAdapterConfigSchema>
|
||||
|
||||
export const openapiOperationParameterSourceSchema = z.union([
|
||||
z.literal('query'),
|
||||
z.literal('header'),
|
||||
z.literal('path'),
|
||||
z.literal('cookie'),
|
||||
z.literal('body'),
|
||||
z.literal('formData')
|
||||
])
|
||||
export type OpenAPIOperationParameterSource = z.infer<
|
||||
typeof openapiOperationParameterSourceSchema
|
||||
>
|
||||
|
||||
export const openapiOperationHttpMethodSchema = z.union([
|
||||
z.literal('get'),
|
||||
z.literal('put'),
|
||||
z.literal('post'),
|
||||
z.literal('delete'),
|
||||
z.literal('patch'),
|
||||
z.literal('trace')
|
||||
])
|
||||
export type OpenAPIOperationHttpMethod = z.infer<
|
||||
typeof openapiOperationHttpMethodSchema
|
||||
>
|
||||
|
||||
export const openapiToolOperationSchema = z.object({
|
||||
operationId: z.string().describe('OpenAPI operationId for the tool'),
|
||||
method: openapiOperationHttpMethodSchema.describe('HTTP method'),
|
||||
path: z.string().describe('HTTP path template'),
|
||||
parameterSources: z
|
||||
.record(z.string(), openapiOperationParameterSourceSchema)
|
||||
.describe(
|
||||
'Mapping from parameter name to HTTP source (query, path, JSON body, etc).'
|
||||
),
|
||||
tags: z.array(z.string()).optional()
|
||||
})
|
||||
export type OpenAPIToolOperation = z.infer<typeof openapiToolOperationSchema>
|
||||
|
||||
export const openapiOriginAdapterSchema = commonOriginAdapterSchema.merge(
|
||||
z.object({
|
||||
/**
|
||||
|
|
@ -121,11 +159,21 @@ export const openapiOriginAdapterSchema = commonOriginAdapterSchema.merge(
|
|||
.string()
|
||||
.describe(
|
||||
'JSON stringified OpenAPI spec describing the origin API server.'
|
||||
)
|
||||
),
|
||||
|
||||
// TODO: Mapping from tool names to OpenAPI operations, with all the info
|
||||
// the Agentic API gateway needs to know at runtime (HTTP method, path,
|
||||
// params, etc).
|
||||
/**
|
||||
* Mapping from tool name to OpenAPI Operation info.
|
||||
*
|
||||
* This is used by the Agentic API gateway to route tools to the correct
|
||||
* origin API operation, along with the HTTP method, path, params, etc.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
toolToOperationMap: z
|
||||
.record(toolNameSchema, openapiToolOperationSchema)
|
||||
.describe(
|
||||
'Mapping from tool name to OpenAPI Operation info. This is used by the Agentic API gateway to route tools to the correct origin API operation, along with the HTTP method, path, params, etc.'
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { z } from 'zod'
|
||||
import { z } from '@hono/zod-openapi'
|
||||
|
||||
export const subjectSchemas = {
|
||||
user: z.object({
|
||||
|
|
|
|||
|
|
@ -134,10 +134,7 @@ export const toolConfigSchema = z
|
|||
'Map of PricingPlan slug to tool config overrides for a given plan. This is useful to customize tool behavior or disable tools completely on different pricing plans.'
|
||||
)
|
||||
|
||||
// TODO: mapping from OpenAPI operationId to tools
|
||||
// TODO?
|
||||
// name
|
||||
// path, httpMethod
|
||||
// examples
|
||||
// headers
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import type { PricingInterval, PricingPlan, PricingPlanList } from './pricing'
|
||||
import type {
|
||||
PricingInterval,
|
||||
PricingPlan,
|
||||
PricingPlanList
|
||||
} from '@agentic/platform-schemas'
|
||||
|
||||
export function getPricingPlansByInterval({
|
||||
pricingInterval,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
export type ParsedFaasIdentifier = {
|
||||
projectIdentifier: string
|
||||
toolPath: string
|
||||
deploymentHash?: string
|
||||
deploymentIdentifier?: string
|
||||
version?: string
|
||||
|
||||
// TODO: Rename to `toolName`?
|
||||
toolPath: string
|
||||
} & (
|
||||
| {
|
||||
deploymentHash: string
|
||||
|
|
|
|||
|
|
@ -251,6 +251,9 @@ importers:
|
|||
|
||||
apps/api:
|
||||
dependencies:
|
||||
'@agentic/platform':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/platform
|
||||
'@agentic/platform-core':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/core
|
||||
|
|
@ -330,6 +333,9 @@ importers:
|
|||
|
||||
apps/gateway:
|
||||
dependencies:
|
||||
'@agentic/platform':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/platform
|
||||
'@agentic/platform-api-client':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/api-client
|
||||
|
|
@ -383,6 +389,9 @@ importers:
|
|||
|
||||
packages/cli:
|
||||
dependencies:
|
||||
'@agentic/platform':
|
||||
specifier: workspace:*
|
||||
version: link:../platform
|
||||
'@agentic/platform-api-client':
|
||||
specifier: workspace:*
|
||||
version: link:../api-client
|
||||
|
|
@ -463,18 +472,27 @@ importers:
|
|||
|
||||
packages/fixtures:
|
||||
dependencies:
|
||||
'@agentic/platform-schemas':
|
||||
'@agentic/platform':
|
||||
specifier: workspace:*
|
||||
version: link:../schemas
|
||||
version: link:../platform
|
||||
|
||||
packages/openapi:
|
||||
dependencies:
|
||||
'@agentic/platform-core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
'@agentic/platform-schemas':
|
||||
specifier: workspace:*
|
||||
version: link:../schemas
|
||||
'@redocly/openapi-core':
|
||||
specifier: 'catalog:'
|
||||
version: 1.34.3(supports-color@10.0.0)
|
||||
camelcase:
|
||||
specifier: ^8.0.0
|
||||
version: 8.0.0
|
||||
decamelize:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
devDependencies:
|
||||
'@hono/node-server':
|
||||
specifier: 'catalog:'
|
||||
|
|
@ -483,7 +501,7 @@ importers:
|
|||
specifier: 'catalog:'
|
||||
version: 4.7.10
|
||||
|
||||
packages/schemas:
|
||||
packages/platform:
|
||||
dependencies:
|
||||
'@agentic/platform-core':
|
||||
specifier: workspace:*
|
||||
|
|
@ -491,6 +509,9 @@ importers:
|
|||
'@agentic/platform-openapi':
|
||||
specifier: workspace:*
|
||||
version: link:../openapi
|
||||
'@agentic/platform-schemas':
|
||||
specifier: workspace:*
|
||||
version: link:../schemas
|
||||
'@agentic/platform-validators':
|
||||
specifier: workspace:*
|
||||
version: link:../validators
|
||||
|
|
@ -500,9 +521,6 @@ importers:
|
|||
'@modelcontextprotocol/sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 1.12.0
|
||||
ms:
|
||||
specifier: 'catalog:'
|
||||
version: 2.1.3
|
||||
semver:
|
||||
specifier: 'catalog:'
|
||||
version: 7.7.2
|
||||
|
|
@ -510,9 +528,6 @@ importers:
|
|||
specifier: 'catalog:'
|
||||
version: 3.25.30
|
||||
devDependencies:
|
||||
'@types/ms':
|
||||
specifier: 'catalog:'
|
||||
version: 2.1.0
|
||||
'@types/semver':
|
||||
specifier: 'catalog:'
|
||||
version: 7.7.0
|
||||
|
|
@ -523,6 +538,28 @@ importers:
|
|||
specifier: 'catalog:'
|
||||
version: 3.24.5(zod@3.25.30)
|
||||
|
||||
packages/schemas:
|
||||
dependencies:
|
||||
'@hono/zod-openapi':
|
||||
specifier: 'catalog:'
|
||||
version: 0.19.6(hono@4.7.10)(zod@3.25.30)
|
||||
ms:
|
||||
specifier: 'catalog:'
|
||||
version: 2.1.3
|
||||
zod:
|
||||
specifier: 'catalog:'
|
||||
version: 3.25.30
|
||||
devDependencies:
|
||||
'@types/ms':
|
||||
specifier: 'catalog:'
|
||||
version: 2.1.0
|
||||
restore-cursor:
|
||||
specifier: 'catalog:'
|
||||
version: 5.1.0
|
||||
zod-to-json-schema:
|
||||
specifier: 'catalog:'
|
||||
version: 3.24.5(zod@3.25.30)
|
||||
|
||||
packages/validators:
|
||||
dependencies:
|
||||
'@paralleldrive/cuid2':
|
||||
|
|
@ -2184,6 +2221,10 @@ packages:
|
|||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
camelcase@8.0.0:
|
||||
resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
caniuse-lite@1.0.30001715:
|
||||
resolution: {integrity: sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==}
|
||||
|
||||
|
|
@ -2345,6 +2386,10 @@ packages:
|
|||
supports-color:
|
||||
optional: true
|
||||
|
||||
decamelize@6.0.0:
|
||||
resolution: {integrity: sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
decircular@0.1.1:
|
||||
resolution: {integrity: sha512-V2Vy+QYSXdgxRPmOZKQWCDf1KQNTUP/Eqswv/3W20gz7+6GB1HTosNrWqK3PqstVpFw/Dd/cGTmXSTKPeOiGVg==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -6135,6 +6180,8 @@ snapshots:
|
|||
|
||||
callsites@3.1.0: {}
|
||||
|
||||
camelcase@8.0.0: {}
|
||||
|
||||
caniuse-lite@1.0.30001715: {}
|
||||
|
||||
chai@5.2.0:
|
||||
|
|
@ -6284,6 +6331,8 @@ snapshots:
|
|||
optionalDependencies:
|
||||
supports-color: 10.0.0
|
||||
|
||||
decamelize@6.0.0: {}
|
||||
|
||||
decircular@0.1.1: {}
|
||||
|
||||
deep-eql@5.0.2: {}
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue