diff --git a/fern/scripts/prepare-openapi.ts b/fern/scripts/prepare-openapi.ts deleted file mode 100644 index 46dd60f3..00000000 --- a/fern/scripts/prepare-openapi.ts +++ /dev/null @@ -1,219 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; - -import { omit } from 'lodash'; -import { execSync } from 'child_process'; -import { merge, isErrorResult } from 'openapi-merge'; -import type { Swagger } from 'atlassian-openapi'; -import { RESTRICTED_ROUTE_BASE_PATHS } from '@letta-cloud/sdk-core'; - -const lettaWebOpenAPIPath = path.join( - __dirname, - '..', - '..', - '..', - 'web', - 'autogenerated', - 'letta-web-openapi.json', -); -const lettaAgentsAPIPath = path.join( - __dirname, - '..', - '..', - 'letta', - 'server', - 'openapi_letta.json', -); - -const lettaWebOpenAPI = JSON.parse( - fs.readFileSync(lettaWebOpenAPIPath, 'utf8'), -) as Swagger.SwaggerV3; -const lettaAgentsAPI = JSON.parse( - fs.readFileSync(lettaAgentsAPIPath, 'utf8'), -) as Swagger.SwaggerV3; - -// removes any routes that are restricted -lettaAgentsAPI.paths = Object.fromEntries( - Object.entries(lettaAgentsAPI.paths).filter(([path]) => - RESTRICTED_ROUTE_BASE_PATHS.every( - (restrictedPath) => !path.startsWith(restrictedPath), - ), - ), -); - -const lettaAgentsAPIWithNoEndslash = Object.keys(lettaAgentsAPI.paths).reduce( - (acc, path) => { - const pathWithoutSlash = path.endsWith('/') - ? path.slice(0, path.length - 1) - : path; - acc[pathWithoutSlash] = lettaAgentsAPI.paths[path]; - return acc; - }, - {} as Swagger.SwaggerV3['paths'], -); - -// remove duplicate paths, delete from letta-web-openapi if it exists in sdk-core -// some paths will have an extra / at the end, so we need to remove that as well -lettaWebOpenAPI.paths = Object.fromEntries( - Object.entries(lettaWebOpenAPI.paths).filter(([path]) => { - const pathWithoutSlash = path.endsWith('/') - ? path.slice(0, path.length - 1) - : path; - return !lettaAgentsAPIWithNoEndslash[pathWithoutSlash]; - }), -); - -const agentStatePathsToOverride: Array<[string, string]> = [ - ['/v1/templates/{project}/{template_version}/agents', '201'], - ['/v1/agents/search', '200'], -]; - -for (const [path, responseCode] of agentStatePathsToOverride) { - if (lettaWebOpenAPI.paths[path]?.post?.responses?.[responseCode]) { - // Get direct reference to the schema object - const responseSchema = - lettaWebOpenAPI.paths[path].post.responses[responseCode]; - const contentSchema = responseSchema.content['application/json'].schema; - - // Replace the entire agents array schema with the reference - if (contentSchema.properties?.agents) { - contentSchema.properties.agents = { - type: 'array', - items: { - $ref: '#/components/schemas/AgentState', - }, - }; - } - } -} - -// go through the paths and remove "user_id"/"actor_id" from the headers -for (const path of Object.keys(lettaAgentsAPI.paths)) { - for (const method of Object.keys(lettaAgentsAPI.paths[path])) { - // @ts-expect-error - a - if (lettaAgentsAPI.paths[path][method]?.parameters) { - // @ts-expect-error - a - lettaAgentsAPI.paths[path][method].parameters = lettaAgentsAPI.paths[ - path - ][method].parameters.filter( - (param: Record) => - param.in !== 'header' || - ( - param.name !== 'user_id' && - param.name !== 'User-Agent' && - param.name !== 'X-Project-Id' && - param.name !== 'X-Letta-Source' && - param.name !== 'X-Stainless-Package-Version' && - !param.name.startsWith('X-Experimental') - ), - ); - } - } -} - -const result = merge([ - { - oas: lettaAgentsAPI, - }, - { - oas: lettaWebOpenAPI, - }, -]); - -if (isErrorResult(result)) { - console.error(`${result.message} (${result.type})`); - process.exit(1); -} - -result.output.openapi = '3.1.0'; -result.output.info = { - title: 'Letta API', - version: '1.0.0', -}; - -result.output.servers = [ - { - url: 'https://app.letta.com', - description: 'Letta Cloud', - }, - { - url: 'http://localhost:8283', - description: 'Self-hosted', - }, -]; - -result.output.components = { - ...result.output.components, - securitySchemes: { - bearerAuth: { - type: 'http', - scheme: 'bearer', - }, - }, -}; - -result.output.security = [ - ...(result.output.security || []), - { - bearerAuth: [], - }, -]; - -// omit all instances of "user_id" from the openapi.json file -function deepOmitPreserveArrays(obj: unknown, key: string): unknown { - if (Array.isArray(obj)) { - return obj.map((item) => deepOmitPreserveArrays(item, key)); - } - - if (typeof obj !== 'object' || obj === null) { - return obj; - } - - if (key in obj) { - return omit(obj, key); - } - - return Object.fromEntries( - Object.entries(obj).map(([k, v]) => [k, deepOmitPreserveArrays(v, key)]), - ); -} - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -result.output.components = deepOmitPreserveArrays( - result.output.components, - 'user_id', -); - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -result.output.components = deepOmitPreserveArrays( - result.output.components, - 'actor_id', -); - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -result.output.components = deepOmitPreserveArrays( - result.output.components, - 'organization_id', -); - -fs.writeFileSync( - path.join(__dirname, '..', 'openapi.json'), - JSON.stringify(result.output, null, 2), -); - -function formatOpenAPIJson() { - const openApiPath = path.join(__dirname, '..', 'openapi.json'); - - try { - execSync(`npx prettier --write "${openApiPath}"`, { stdio: 'inherit' }); - console.log('Successfully formatted openapi.json with Prettier'); - } catch (error) { - console.error('Error formatting openapi.json:', error); - process.exit(1); - } -} - -formatOpenAPIJson(); diff --git a/letta/__init__.py b/letta/__init__.py index b091ed4b..71fe087c 100644 --- a/letta/__init__.py +++ b/letta/__init__.py @@ -5,7 +5,7 @@ try: __version__ = version("letta") except PackageNotFoundError: # Fallback for development installations - __version__ = "0.15.1" + __version__ = "0.16.0" if os.environ.get("LETTA_VERSION"): __version__ = os.environ["LETTA_VERSION"] diff --git a/pyproject.toml b/pyproject.toml index 69862802..66dd0b26 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "letta" -version = "0.15.2" +version = "0.16.0" description = "Create LLM agents with long-term memory and custom tools" authors = [ {name = "Letta Team", email = "contact@letta.com"}, diff --git a/uv.lock b/uv.lock index b75698bf..43a0b801 100644 --- a/uv.lock +++ b/uv.lock @@ -2335,7 +2335,7 @@ wheels = [ [[package]] name = "letta" -version = "0.15.2" +version = "0.16.0" source = { editable = "." } dependencies = [ { name = "aiomultiprocess" },