feat: add letta/auto and letta/auto-fast model support (#1259)

Co-authored-by: Letta Code <noreply@letta.com>
This commit is contained in:
cthomas
2026-03-04 12:16:30 -08:00
committed by GitHub
parent fbade3aee7
commit 2edbe0566d
4 changed files with 153 additions and 4 deletions

View File

@@ -0,0 +1,57 @@
/**
* Live API regression test for Cloud model availability.
*
* Runs only when:
* - LETTA_API_KEY is set
* - LETTA_BASE_URL points to Letta Cloud (api.letta.com)
*/
import { describe, expect, test } from "bun:test";
import Letta from "@letta-ai/letta-client";
const LETTA_API_KEY = process.env.LETTA_API_KEY;
const LETTA_BASE_URL = process.env.LETTA_BASE_URL || "https://api.letta.com";
function isCloudBaseUrl(value: string): boolean {
try {
return new URL(value).hostname === "api.letta.com";
} catch {
return value.includes("api.letta.com");
}
}
const describeIntegration =
LETTA_API_KEY && isCloudBaseUrl(LETTA_BASE_URL) ? describe : describe.skip;
async function listModelHandlesWithRetry(
client: Letta,
maxAttempts = 3,
): Promise<string[]> {
let lastError: unknown;
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
try {
const models = await client.models.list();
return models.map((m) => m.handle).filter((h): h is string => Boolean(h));
} catch (error) {
lastError = error;
if (attempt < maxAttempts) {
await new Promise((resolve) => setTimeout(resolve, 500 * attempt));
}
}
}
throw lastError;
}
describeIntegration("cloud models list", () => {
test("includes letta/auto handle", async () => {
const client = new Letta({
baseURL: LETTA_BASE_URL,
apiKey: LETTA_API_KEY,
});
const handles = await listModelHandlesWithRetry(client);
expect(handles).toContain("letta/auto");
});
});

View File

@@ -0,0 +1,55 @@
import { describe, expect, test } from "bun:test";
import { filterModelsByAvailabilityForSelector } from "../../cli/components/ModelSelector";
type StubModel = { handle: string; label: string };
const MODELS: StubModel[] = [
{ handle: "letta/auto", label: "Auto" },
{ handle: "letta/auto-fast", label: "Auto Fast" },
{ handle: "anthropic/claude-sonnet-4-6", label: "Sonnet 4.6" },
];
describe("ModelSelector availability gating", () => {
test("includes letta/auto when API availability includes it", () => {
const availableHandles = new Set([
"letta/auto",
"anthropic/claude-sonnet-4-6",
]);
const result = filterModelsByAvailabilityForSelector(
MODELS,
availableHandles,
Array.from(availableHandles),
);
expect(result.map((m) => m.handle)).toContain("letta/auto");
});
test("excludes letta/auto when API availability does not include it", () => {
const availableHandles = new Set(["anthropic/claude-sonnet-4-6"]);
const result = filterModelsByAvailabilityForSelector(
MODELS,
availableHandles,
Array.from(availableHandles),
);
expect(result.map((m) => m.handle)).not.toContain("letta/auto");
});
test("fallback mode hides letta/auto unless explicitly present in allApiHandles", () => {
const hiddenResult = filterModelsByAvailabilityForSelector(MODELS, null, [
"anthropic/claude-sonnet-4-6",
]);
expect(hiddenResult.map((m) => m.handle)).not.toContain("letta/auto");
expect(hiddenResult.map((m) => m.handle)).toContain(
"anthropic/claude-sonnet-4-6",
);
const shownResult = filterModelsByAvailabilityForSelector(MODELS, null, [
"letta/auto",
"anthropic/claude-sonnet-4-6",
]);
expect(shownResult.map((m) => m.handle)).toContain("letta/auto");
});
});