From 4448398c3096bc715a71b17eb7907b91365857c9 Mon Sep 17 00:00:00 2001 From: Anar Kafkas Date: Fri, 26 Jun 2026 10:44:41 +0300 Subject: [PATCH 1/4] Replace DEFAULT_FDR_LAMBDA_DOCS_ORIGIN with DEFAULT_FAI_ORIGIN --- packages/cli/cli-v2/build.dev.mjs | 2 +- packages/cli/cli/build.dev.mjs | 2 +- packages/cli/cli/build.prod.mjs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cli/cli-v2/build.dev.mjs b/packages/cli/cli-v2/build.dev.mjs index 37b234b20c3a..60bf148e0805 100644 --- a/packages/cli/cli-v2/build.dev.mjs +++ b/packages/cli/cli-v2/build.dev.mjs @@ -9,7 +9,7 @@ buildCli({ DEFAULT_FIDDLE_ORIGIN: "https://fiddle-coordinator.buildwithfern.com", DEFAULT_VENUS_ORIGIN: "https://venus.buildwithfern.com", DEFAULT_FDR_ORIGIN: "https://registry.buildwithfern.com", - DEFAULT_FDR_LAMBDA_DOCS_ORIGIN: "https://ykq45y6fvnszd35iv5yuuatkze0rpwuz.lambda-url.us-east-1.on.aws", + DEFAULT_FAI_ORIGIN: "https://fai.buildwithfern.com", VENUS_AUDIENCE: "venus-prod", LOCAL_STORAGE_FOLDER: ".fern", POSTHOG_API_KEY: process.env.POSTHOG_API_KEY ?? "", diff --git a/packages/cli/cli/build.dev.mjs b/packages/cli/cli/build.dev.mjs index 71a6a4090e9b..a9b03b63614c 100644 --- a/packages/cli/cli/build.dev.mjs +++ b/packages/cli/cli/build.dev.mjs @@ -9,7 +9,7 @@ buildCli({ DEFAULT_FIDDLE_ORIGIN: "https://fiddle-coordinator-dev2.buildwithfern.com", DEFAULT_VENUS_ORIGIN: "https://venus-dev2.buildwithfern.com", DEFAULT_FDR_ORIGIN: "https://registry-dev2.buildwithfern.com", - DEFAULT_FDR_LAMBDA_DOCS_ORIGIN: "https://ykq45y6fvnszd35iv5yuuatkze0rpwuz.lambda-url.us-east-1.on.aws", + DEFAULT_FAI_ORIGIN: "https://fai-dev2.buildwithfern.com", VENUS_AUDIENCE: "venus-dev", FERN_DASHBOARD_URL_DEFAULT: "https://dashboard-dev.buildwithfern.com", LOCAL_STORAGE_FOLDER: ".fern-dev", diff --git a/packages/cli/cli/build.prod.mjs b/packages/cli/cli/build.prod.mjs index 73a0dcd40dd6..2b88ab1dae0b 100644 --- a/packages/cli/cli/build.prod.mjs +++ b/packages/cli/cli/build.prod.mjs @@ -9,7 +9,7 @@ buildCli({ DEFAULT_FIDDLE_ORIGIN: "https://fiddle-coordinator.buildwithfern.com", DEFAULT_VENUS_ORIGIN: "https://venus.buildwithfern.com", DEFAULT_FDR_ORIGIN: "https://registry.buildwithfern.com", - DEFAULT_FDR_LAMBDA_DOCS_ORIGIN: "https://ykq45y6fvnszd35iv5yuuatkze0rpwuz.lambda-url.us-east-1.on.aws", + DEFAULT_FAI_ORIGIN: "https://fai.buildwithfern.com", VENUS_AUDIENCE: "venus-prod", FERN_DASHBOARD_URL_DEFAULT: "https://dashboard.buildwithfern.com", LOCAL_STORAGE_FOLDER: ".fern", From d72a78654e1252a7c417eacb13c897fe78522c6f Mon Sep 17 00:00:00 2001 From: Anar Kafkas Date: Fri, 26 Jun 2026 10:45:40 +0300 Subject: [PATCH 2/4] Replace lambda client with fai client --- .../{lambdaClient.ts => faiClient.ts} | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) rename packages/cli/register/src/ai-example-enhancer/{lambdaClient.ts => faiClient.ts} (93%) diff --git a/packages/cli/register/src/ai-example-enhancer/lambdaClient.ts b/packages/cli/register/src/ai-example-enhancer/faiClient.ts similarity index 93% rename from packages/cli/register/src/ai-example-enhancer/lambdaClient.ts rename to packages/cli/register/src/ai-example-enhancer/faiClient.ts index 78ba53060dcc..77a96c010006 100644 --- a/packages/cli/register/src/ai-example-enhancer/lambdaClient.ts +++ b/packages/cli/register/src/ai-example-enhancer/faiClient.ts @@ -17,10 +17,10 @@ interface VenusJwtResponse { expiresAt: string; } -export class LambdaExampleEnhancer { +export class FaiExampleEnhancer { private config: AIEnhancerResolvedConfig; private context: TaskContext; - private lambdaOrigin: string; + private faiOrigin: string; private venusOrigin: string; private token: FernToken; private jwtPromise: Promise | undefined; @@ -38,16 +38,16 @@ export class LambdaExampleEnhancer { }; this.context = context; - // Get Lambda origin - throw error if not configured - const lambdaOrigin = process.env.DEFAULT_FDR_LAMBDA_DOCS_ORIGIN; - if (!lambdaOrigin) { + // Get FAI origin - throw error if not configured + const faiOrigin = process.env.DEFAULT_FAI_ORIGIN; + if (!faiOrigin) { throw new CliError({ message: - "DEFAULT_FDR_LAMBDA_DOCS_ORIGIN environment variable is not set. AI example enhancement requires this to be configured.", + "DEFAULT_FAI_ORIGIN environment variable is not set. AI example enhancement requires this to be configured.", code: CliError.Code.EnvironmentError }); } - this.lambdaOrigin = lambdaOrigin; + this.faiOrigin = faiOrigin; // Get Venus origin for JWT exchange this.venusOrigin = process.env.DEFAULT_VENUS_ORIGIN ?? "https://venus.buildwithfern.com"; @@ -175,7 +175,7 @@ export class LambdaExampleEnhancer { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { this.context.logger.debug( - `Enhancing example for ${request.method} ${request.endpointPath} via lambda (attempt ${attempt}/${maxAttempts})` + `Enhancing example for ${request.method} ${request.endpointPath} via FAI (attempt ${attempt}/${maxAttempts})` ); const requestBody = { @@ -203,7 +203,7 @@ export class LambdaExampleEnhancer { )}` ); - const response = await fetch(`${this.lambdaOrigin}/v2/registry/ai/enhance-example`, { + const response = await fetch(`${this.faiOrigin}/examples/enhance`, { method: "POST", headers: { "Content-Type": "application/json", @@ -216,7 +216,7 @@ export class LambdaExampleEnhancer { if (!response.ok) { const errorText = await response.text(); throw new CliError({ - message: `Lambda returned ${response.status}: ${errorText || response.statusText}`, + message: `FAI returned ${response.status}: ${errorText || response.statusText}`, code: CliError.Code.NetworkError }); } From 6aeff7c28194219ee488c41214f1ac7411224691 Mon Sep 17 00:00:00 2001 From: Anar Kafkas Date: Fri, 26 Jun 2026 10:46:24 +0300 Subject: [PATCH 3/4] Use fai enhancer --- .../__test__/backfillMissingFields.test.ts | 26 ++++++++--------- .../enhanceExamplesWithAI.ts | 28 +++++++++---------- .../writeAiExamplesOverride.ts | 2 +- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/cli/register/src/ai-example-enhancer/__test__/backfillMissingFields.test.ts b/packages/cli/register/src/ai-example-enhancer/__test__/backfillMissingFields.test.ts index 7b9ffda2593d..ce4126418cb1 100644 --- a/packages/cli/register/src/ai-example-enhancer/__test__/backfillMissingFields.test.ts +++ b/packages/cli/register/src/ai-example-enhancer/__test__/backfillMissingFields.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { backfillMissingFields, unwrapLambdaBodyEnvelope } from "../enhanceExamplesWithAI.js"; +import { backfillMissingFields, unwrapBodyEnvelope } from "../enhanceExamplesWithAI.js"; describe("backfillMissingFields", () => { it("backfills missing fields from original into enhanced", () => { @@ -79,30 +79,30 @@ describe("backfillMissingFields", () => { }); }); -describe("unwrapLambdaBodyEnvelope", () => { +describe("unwrapBodyEnvelope", () => { it("unwraps {body: {...}} envelope", () => { const wrapped = { body: { channelIds: [101, 202] } }; - const result = unwrapLambdaBodyEnvelope(wrapped); + const result = unwrapBodyEnvelope(wrapped); expect(result.wasWrapped).toBe(true); expect(result.inner).toEqual({ channelIds: [101, 202] }); }); it("unwraps even when body is not the only key", () => { const multiKey = { body: { a: 1 }, statusCode: 200 }; - const result = unwrapLambdaBodyEnvelope(multiKey); + const result = unwrapBodyEnvelope(multiKey); expect(result.wasWrapped).toBe(true); expect(result.inner).toEqual({ a: 1 }); }); it("does not unwrap non-objects", () => { - expect(unwrapLambdaBodyEnvelope("hello")).toEqual({ wasWrapped: false, inner: "hello" }); - expect(unwrapLambdaBodyEnvelope(null)).toEqual({ wasWrapped: false, inner: null }); - expect(unwrapLambdaBodyEnvelope(42)).toEqual({ wasWrapped: false, inner: 42 }); + expect(unwrapBodyEnvelope("hello")).toEqual({ wasWrapped: false, inner: "hello" }); + expect(unwrapBodyEnvelope(null)).toEqual({ wasWrapped: false, inner: null }); + expect(unwrapBodyEnvelope(42)).toEqual({ wasWrapped: false, inner: 42 }); }); it("does not unwrap arrays", () => { const arr = [1, 2, 3]; - expect(unwrapLambdaBodyEnvelope(arr)).toEqual({ wasWrapped: false, inner: arr }); + expect(unwrapBodyEnvelope(arr)).toEqual({ wasWrapped: false, inner: arr }); }); it("backfills correctly through body envelope", () => { @@ -114,7 +114,7 @@ describe("unwrapLambdaBodyEnvelope", () => { channelIds: [1] }; - const unwrapped = unwrapLambdaBodyEnvelope(wrapped); + const unwrapped = unwrapBodyEnvelope(wrapped); expect(unwrapped.wasWrapped).toBe(true); const backfilled = backfillMissingFields(unwrapped.inner, original); @@ -127,11 +127,11 @@ describe("unwrapLambdaBodyEnvelope", () => { }); it("treats body as envelope only when original does NOT have body key", () => { - // Lambda envelope case: original has no "body" key → unwrap + // Envelope case: original has no "body" key → unwrap const envelopeEnhanced = { body: { channelIds: [101] } }; const originalNoBody = { channelIds: [1], firstName: "string" }; - const unwrapped = unwrapLambdaBodyEnvelope(envelopeEnhanced); + const unwrapped = unwrapBodyEnvelope(envelopeEnhanced); expect(unwrapped.wasWrapped).toBe(true); // Caller should check originalHasBody=false → treat as envelope @@ -139,8 +139,8 @@ describe("unwrapLambdaBodyEnvelope", () => { const schemaEnhanced = { body: "Hello world", subject: "Greetings" }; const originalWithBody = { body: "string", subject: "string", to: "string" }; - const unwrappedSchema = unwrapLambdaBodyEnvelope(schemaEnhanced); - // unwrapLambdaBodyEnvelope still returns wasWrapped=true (it's a detection helper), + const unwrappedSchema = unwrapBodyEnvelope(schemaEnhanced); + // unwrapBodyEnvelope still returns wasWrapped=true (it's a detection helper), // but the caller checks originalHasBody and skips the envelope path. expect(unwrappedSchema.wasWrapped).toBe(true); diff --git a/packages/cli/register/src/ai-example-enhancer/enhanceExamplesWithAI.ts b/packages/cli/register/src/ai-example-enhancer/enhanceExamplesWithAI.ts index 766937fb1a39..3c753bc42b32 100644 --- a/packages/cli/register/src/ai-example-enhancer/enhanceExamplesWithAI.ts +++ b/packages/cli/register/src/ai-example-enhancer/enhanceExamplesWithAI.ts @@ -9,7 +9,7 @@ import * as yaml from "js-yaml"; import { OpenAPIV3 } from "openapi-types"; import { join } from "path"; import { filterRequestBody, isFdrTypedValueWrapper, unwrapExampleValue } from "./filterHelpers.js"; -import { LambdaExampleEnhancer } from "./lambdaClient.js"; +import { FaiExampleEnhancer } from "./faiClient.js"; import { SpinnerStatusCoordinator } from "./spinnerStatusCoordinator.js"; import { AIExampleEnhancerConfig, @@ -517,7 +517,7 @@ async function performAIEnhancement( sourceSpecs?: OpenAPISourceSpec[], apiName?: string ): Promise { - const enhancer = new LambdaExampleEnhancer(config, context, token, organizationId); + const enhancer = new FaiExampleEnhancer(config, context, token, organizationId); const circuitBreaker = new CircuitBreaker(); let openApiSpec: string | undefined; @@ -679,7 +679,7 @@ async function performAIEnhancement( async function enhancePackageExamples( apiDefinition: FdrCjsSdk.api.v1.register.ApiDefinition, - enhancer: LambdaExampleEnhancer, + enhancer: FaiExampleEnhancer, context: TaskContext, organizationId: string, stats: { count: number; total: number }, @@ -893,7 +893,7 @@ function collectWorkItems( async function processEndpointsConcurrently( allWorkItems: (EndpointWorkItem & { packageId?: string })[], - enhancer: LambdaExampleEnhancer, + enhancer: FaiExampleEnhancer, context: TaskContext, organizationId: string, stats: { count: number; total: number }, @@ -919,7 +919,7 @@ async function processEndpointsConcurrently( const maxConcurrentRequests = parseInt(process.env.FERN_AI_MAX_CONCURRENT || "25", 10); context.logger.debug( - `Processing ${allWorkItems.length} endpoints with max ${maxConcurrentRequests} concurrent Lambda calls using rolling window queue` + `Processing ${allWorkItems.length} endpoints with max ${maxConcurrentRequests} concurrent FAI calls using rolling window queue` ); // Check circuit breaker before processing @@ -1055,7 +1055,7 @@ async function processEndpointsConcurrently( async function processEndpoint( workItem: EndpointWorkItem & { packageId?: string }, - enhancer: LambdaExampleEnhancer, + enhancer: FaiExampleEnhancer, context: TaskContext, organizationId: string, stats: { count: number; total: number }, @@ -1111,7 +1111,7 @@ async function processEndpoint( openApiSpec: prunedOpenApiSpec }; - // Single attempt - Lambda client handles retries internally (1 retry total) + // Single attempt - FAI client handles retries internally (1 retry total) try { const result = await enhancer.enhanceExample(request); @@ -1119,14 +1119,14 @@ async function processEndpoint( circuitBreaker?.recordSuccess(); // Backfill any required fields the AI may have missed from the original auto-generated example. - // The AI Lambda may not fully resolve nested allOf chains, producing partial examples. - // Handle the Lambda's {body: {...}} envelope if present, but only when the original + // The enhancement service may not fully resolve nested allOf chains, producing partial examples. + // Handle a {body: {...}} envelope if present, but only when the original // does NOT have a "body" key (distinguishes envelope from schemas with a literal body field). if (result.enhancedRequestExample != null && request.originalRequestExample != null) { - const unwrapped = unwrapLambdaBodyEnvelope(result.enhancedRequestExample); + const unwrapped = unwrapBodyEnvelope(result.enhancedRequestExample); const originalHasBody = isObjectWithKey(request.originalRequestExample, "body"); if (unwrapped.wasWrapped && !originalHasBody) { - // Lambda envelope detected — backfill the inner value directly. + // Envelope detected — backfill the inner value directly. // Do NOT re-wrap: the IR path only handles FDR {type,value} wrappers, // and writeAiExamplesOverride has its own unwrap logic. result.enhancedRequestExample = backfillMissingFields(unwrapped.inner, request.originalRequestExample); @@ -1668,10 +1668,10 @@ function isObjectWithKey(value: unknown, key: string): boolean { } /** - * Detects and unwraps the Lambda's `{body: {...}}` envelope format. - * The Lambda sometimes wraps the request example in a "body" key. + * Detects and unwraps a `{body: {...}}` envelope format. + * The enhancement service sometimes wraps the request example in a "body" key. */ -export function unwrapLambdaBodyEnvelope(value: unknown): { wasWrapped: boolean; inner: unknown } { +export function unwrapBodyEnvelope(value: unknown): { wasWrapped: boolean; inner: unknown } { if (typeof value === "object" && value !== null && !Array.isArray(value) && "body" in value) { return { wasWrapped: true, inner: (value as Record).body }; } diff --git a/packages/cli/register/src/ai-example-enhancer/writeAiExamplesOverride.ts b/packages/cli/register/src/ai-example-enhancer/writeAiExamplesOverride.ts index 0b81287fb0ac..2ae72d5ec92c 100644 --- a/packages/cli/register/src/ai-example-enhancer/writeAiExamplesOverride.ts +++ b/packages/cli/register/src/ai-example-enhancer/writeAiExamplesOverride.ts @@ -131,7 +131,7 @@ export async function writeAiExamplesOverride({ } // Extract the inner value if the request body is wrapped in a "body" key - // The Lambda response sometimes wraps the request in { "body": { ... } } + // The enhancement service response sometimes wraps the request in { "body": { ... } } let requestBodyToProcess = example.requestBody; if ( requestBodyToProcess !== null && From c6ba6cd1c5adf561e8e59cdecd831644befd7200 Mon Sep 17 00:00:00 2001 From: Anar Kafkas Date: Fri, 26 Jun 2026 10:55:54 +0300 Subject: [PATCH 4/4] Fix format --- .../register/src/ai-example-enhancer/enhanceExamplesWithAI.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/register/src/ai-example-enhancer/enhanceExamplesWithAI.ts b/packages/cli/register/src/ai-example-enhancer/enhanceExamplesWithAI.ts index 3c753bc42b32..2cea007cf463 100644 --- a/packages/cli/register/src/ai-example-enhancer/enhanceExamplesWithAI.ts +++ b/packages/cli/register/src/ai-example-enhancer/enhanceExamplesWithAI.ts @@ -8,8 +8,8 @@ import { readFile, writeFile } from "fs/promises"; import * as yaml from "js-yaml"; import { OpenAPIV3 } from "openapi-types"; import { join } from "path"; -import { filterRequestBody, isFdrTypedValueWrapper, unwrapExampleValue } from "./filterHelpers.js"; import { FaiExampleEnhancer } from "./faiClient.js"; +import { filterRequestBody, isFdrTypedValueWrapper, unwrapExampleValue } from "./filterHelpers.js"; import { SpinnerStatusCoordinator } from "./spinnerStatusCoordinator.js"; import { AIExampleEnhancerConfig,