Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/langsmith-apac-region.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"openwiki": patch
---

Add LangSmith APAC region (`apac.api.smith.langchain.com`) to setup and allowlisted hosts.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ Connector secrets are referenced by env var name and stored in `~/.openwiki/.env

The connectors above feed a `personal` wiki. The **LangSmith** connector instead enriches a `code` wiki: it pulls recent LangSmith traces (tool calls, outcomes, and latency) for the projects you choose through the official LangSmith SDK, so a repository's docs reflect how its code actually behaves at runtime, not just what the source says.

Configure it during `openwiki --init` in `code` mode. From the source menu, add LangSmith, pick your workspace region (US or EU), and list the projects to document. OpenWiki writes a committed `openwiki/.langsmith.json` that names the workspaces and projects (never the key itself), so every teammate and CI run documents the same set. The API key is read from the environment:
Configure it during `openwiki --init` in `code` mode. From the source menu, add LangSmith, pick your workspace region (US, EU, or APAC), and list the projects to document. OpenWiki writes a committed `openwiki/.langsmith.json` that names the workspaces and projects (never the key itself), so every teammate and CI run documents the same set. The API key is read from the environment:

```sh
OPENWIKI_LANGSMITH_API_KEY="<your-langsmith-key>"
Expand All @@ -136,7 +136,7 @@ OPENWIKI_LANGSMITH_API_KEY="<your-langsmith-key>"
Locally the setup wizard saves this to `~/.openwiki/.env`. In CI, set it as a repository secret and export it for the run.

> [!NOTE]
> A LangSmith key is workspace- and region-bound. To document projects across more than one workspace, add an entry per workspace, each with its own key named `OPENWIKI_LANGSMITH_API_KEY_2`, `OPENWIKI_LANGSMITH_API_KEY_3`, and so on. The connector only talks to the official US (`api.smith.langchain.com`) and EU (`eu.api.smith.langchain.com`) hosts.
> A LangSmith key is workspace- and region-bound. To document projects across more than one workspace, add an entry per workspace, each with its own key named `OPENWIKI_LANGSMITH_API_KEY_2`, `OPENWIKI_LANGSMITH_API_KEY_3`, and so on. The connector only talks to the official US (`api.smith.langchain.com`), EU (`eu.api.smith.langchain.com`), and APAC (`apac.api.smith.langchain.com`) hosts.

## How it stays yours

Expand Down
3 changes: 2 additions & 1 deletion src/connectors/sources/langsmith/repo-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface LangSmithWorkspaceConfig {
projects: LangSmithProjectConfig[];

/**
* Non-default API host for EU workspaces.
* Non-default API host for EU/APAC workspaces.
*
* @default the connector's default host (https://api.smith.langchain.com)
*/
Expand All @@ -49,6 +49,7 @@ export interface LangSmithRepoConfig {
const ALLOWED_API_HOSTS = new Set([
"api.smith.langchain.com",
"eu.api.smith.langchain.com",
"apac.api.smith.langchain.com",
]);

/**
Expand Down
29 changes: 23 additions & 6 deletions src/connectors/sources/langsmith/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,32 @@ import {
} from "./repo-config.js";

/**
* LangSmith workspace region. Maps to the two official API hosts the connector
* LangSmith workspace region. Maps to the official API hosts the connector
* is allowlisted to talk to; the wizard offers this instead of a raw URL.
*/
export type LangSmithRegion = "us" | "eu";
export type LangSmithRegion = "us" | "eu" | "apac";

/**
* EU host, written to apiBaseUrl for EU workspaces. The US host is the connector
* default, so it is left out of the file entirely.
* Non-US hosts, written to apiBaseUrl for that region. The US host is the
* connector default, so it is left out of the file entirely.
*/
const EU_API_BASE_URL = "https://eu.api.smith.langchain.com";
const APAC_API_BASE_URL = "https://apac.api.smith.langchain.com";

const API_BASE_URL_BY_REGION: Record<Exclude<LangSmithRegion, "us">, string> = {
apac: APAC_API_BASE_URL,
eu: EU_API_BASE_URL,
};

function regionFromApiBaseUrl(apiBaseUrl: string | undefined): LangSmithRegion {
if (apiBaseUrl === EU_API_BASE_URL) {
return "eu";
}
if (apiBaseUrl === APAC_API_BASE_URL) {
return "apac";
}
return "us";
}
/**
* Base env var name for the first workspace's key. Additional workspaces get
* OPENWIKI_LANGSMITH_API_KEY_<n>.
Expand Down Expand Up @@ -54,7 +69,7 @@ export async function loadLangSmithSetup(
return (config?.workspaces ?? []).map((workspace) => ({
apiKeyEnv: workspace.apiKeyEnv,
projects: workspace.projects.map((project) => project.name),
region: workspace.apiBaseUrl === EU_API_BASE_URL ? "eu" : "us",
region: regionFromApiBaseUrl(workspace.apiBaseUrl),
}));
}

Expand Down Expand Up @@ -87,7 +102,9 @@ export async function saveLangSmithSetup(
cleaned.push({
apiKeyEnv: workspace.apiKeyEnv,
projects,
...(workspace.region === "eu" ? { apiBaseUrl: EU_API_BASE_URL } : {}),
...(workspace.region === "us"
? {}
: { apiBaseUrl: API_BASE_URL_BY_REGION[workspace.region] }),
});
}
if (cleaned.length === 0 && !existing) {
Expand Down
6 changes: 6 additions & 0 deletions src/credentials.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,12 @@ const LANGSMITH_REGION_OPTIONS = [
id: "eu",
name: "EU",
},
{
description: "APAC workspaces.",
host: "https://apac.api.smith.langchain.com",
id: "apac",
name: "APAC",
},
] as const satisfies readonly {
description: string;
host: string;
Expand Down
3 changes: 3 additions & 0 deletions test/langsmith-repo-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ describe("sanitizeLangSmithApiBaseUrl", () => {
expect(
sanitizeLangSmithApiBaseUrl("https://eu.api.smith.langchain.com"),
).toBe("https://eu.api.smith.langchain.com");
expect(
sanitizeLangSmithApiBaseUrl("https://apac.api.smith.langchain.com"),
).toBe("https://apac.api.smith.langchain.com");
});

test.each([
Expand Down
23 changes: 22 additions & 1 deletion test/langsmith-setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {

const REPO = "/repo";
const EU = "https://eu.api.smith.langchain.com";
const APAC = "https://apac.api.smith.langchain.com";

beforeEach(() => {
vi.clearAllMocks();
Expand All @@ -35,6 +36,11 @@ describe("loadLangSmithSetup", () => {
apiKeyEnv: "OPENWIKI_LANGSMITH_API_KEY_2",
projects: [{ name: "c" }],
},
{
apiBaseUrl: APAC,
apiKeyEnv: "OPENWIKI_LANGSMITH_API_KEY_3",
projects: [{ name: "d" }],
},
],
});

Expand All @@ -49,6 +55,11 @@ describe("loadLangSmithSetup", () => {
projects: ["c"],
region: "eu",
},
{
apiKeyEnv: "OPENWIKI_LANGSMITH_API_KEY_3",
projects: ["d"],
region: "apac",
},
]);
});

Expand All @@ -60,7 +71,7 @@ describe("loadLangSmithSetup", () => {
});

describe("saveLangSmithSetup", () => {
test("writes workspaces, US omitting apiBaseUrl and EU including it", async () => {
test("writes workspaces, US omitting apiBaseUrl and EU/APAC including it", async () => {
vi.mocked(readLangSmithRepoConfig).mockResolvedValue(undefined);

await saveLangSmithSetup(REPO, [
Expand All @@ -74,6 +85,11 @@ describe("saveLangSmithSetup", () => {
projects: ["b"],
region: "eu",
},
{
apiKeyEnv: "OPENWIKI_LANGSMITH_API_KEY_3",
projects: ["c"],
region: "apac",
},
]);

expect(writeLangSmithRepoConfig).toHaveBeenCalledWith(REPO, {
Expand All @@ -84,6 +100,11 @@ describe("saveLangSmithSetup", () => {
apiKeyEnv: "OPENWIKI_LANGSMITH_API_KEY_2",
projects: [{ name: "b" }],
},
{
apiBaseUrl: APAC,
apiKeyEnv: "OPENWIKI_LANGSMITH_API_KEY_3",
projects: [{ name: "c" }],
},
],
});
});
Expand Down