Skip to content

Commit 65825eb

Browse files
authored
Merge pull request #123 from cacheplane/codex/single-dev-deployment
Collapse cockpit backends into one LangSmith deployment
2 parents 8d74b2b + 8018349 commit 65825eb

14 files changed

Lines changed: 552 additions & 235 deletions

File tree

.github/workflows/ci.yml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,19 @@ jobs:
150150

151151
deploy:
152152
name: Deploy → Vercel
153-
needs: [library, website, cockpit, cockpit-examples-build, cockpit-smoke, cockpit-secret-integration, cockpit-deploy-smoke, chat-agent-smoke, cockpit-e2e, website-e2e]
153+
needs:
154+
[
155+
library,
156+
website,
157+
cockpit,
158+
cockpit-examples-build,
159+
cockpit-smoke,
160+
cockpit-secret-integration,
161+
cockpit-deploy-smoke,
162+
chat-agent-smoke,
163+
cockpit-e2e,
164+
website-e2e,
165+
]
154166
runs-on: ubuntu-latest
155167
# Only deploy on pushes to main, not on pull requests
156168
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
@@ -297,8 +309,10 @@ jobs:
297309
cache: npm
298310
- run: npm ci
299311
- run: npx playwright install --with-deps chromium
300-
- name: Verify LangGraph backends
301-
run: npx tsx scripts/verify-langgraph-deployments.ts
312+
- name: Verify shared LangGraph backend
313+
run: npx tsx scripts/verify-shared-deployment.ts
314+
env:
315+
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
302316
- name: Run production smoke tests
303317
run: npx playwright test apps/cockpit/e2e/production-smoke.spec.ts --reporter=list
304318
env:

.github/workflows/deploy-langgraph.yml

Lines changed: 23 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -4,89 +4,32 @@ on:
44
push:
55
branches: [main]
66
paths:
7-
- 'cockpit/**/python/**'
7+
- 'cockpit/langgraph/**/python/**'
8+
- 'cockpit/deep-agents/**/python/**'
9+
- 'apps/cockpit/scripts/capability-registry.ts'
10+
- 'scripts/generate-shared-deployment-config.ts'
11+
- 'deployments/shared-dev/langgraph.json'
812
workflow_dispatch:
913
inputs:
1014
capability:
11-
description: 'Capability path (e.g., langgraph/streaming)'
15+
description: 'Deprecated and ignored by the shared deployment flow'
1216
required: false
1317
type: string
1418

1519
jobs:
1620
deploy:
17-
name: Deploy to LangGraph Cloud
21+
name: Deploy shared cockpit-dev to LangGraph Cloud
1822
runs-on: ubuntu-latest
19-
strategy:
20-
fail-fast: false
21-
matrix:
22-
include:
23-
- name: streaming
24-
path: cockpit/langgraph/streaming/python
25-
- name: persistence
26-
path: cockpit/langgraph/persistence/python
27-
- name: interrupts
28-
path: cockpit/langgraph/interrupts/python
29-
- name: memory
30-
path: cockpit/langgraph/memory/python
31-
- name: durable-execution
32-
path: cockpit/langgraph/durable-execution/python
33-
- name: subgraphs
34-
path: cockpit/langgraph/subgraphs/python
35-
- name: time-travel
36-
path: cockpit/langgraph/time-travel/python
37-
- name: deployment-runtime
38-
path: cockpit/langgraph/deployment-runtime/python
39-
- name: planning
40-
path: cockpit/deep-agents/planning/python
41-
- name: filesystem
42-
path: cockpit/deep-agents/filesystem/python
43-
- name: da-subagents
44-
path: cockpit/deep-agents/subagents/python
45-
- name: da-memory
46-
path: cockpit/deep-agents/memory/python
47-
- name: skills
48-
path: cockpit/deep-agents/skills/python
49-
- name: sandboxes
50-
path: cockpit/deep-agents/sandboxes/python
51-
# Chat capabilities
52-
- name: c-a2ui
53-
path: cockpit/chat/a2ui/python
54-
- name: c-debug
55-
path: cockpit/chat/debug/python
56-
- name: c-generative-ui
57-
path: cockpit/chat/generative-ui/python
58-
- name: c-input
59-
path: cockpit/chat/input/python
60-
- name: c-interrupts
61-
path: cockpit/chat/interrupts/python
62-
- name: c-messages
63-
path: cockpit/chat/messages/python
64-
- name: c-subagents
65-
path: cockpit/chat/subagents/python
66-
- name: c-theming
67-
path: cockpit/chat/theming/python
68-
- name: c-threads
69-
path: cockpit/chat/threads/python
70-
- name: c-timeline
71-
path: cockpit/chat/timeline/python
72-
- name: c-tool-calls
73-
path: cockpit/chat/tool-calls/python
74-
# Render capabilities
75-
- name: r-computed-functions
76-
path: cockpit/render/computed-functions/python
77-
- name: r-element-rendering
78-
path: cockpit/render/element-rendering/python
79-
- name: r-registry
80-
path: cockpit/render/registry/python
81-
- name: r-repeat-loops
82-
path: cockpit/render/repeat-loops/python
83-
- name: r-spec-rendering
84-
path: cockpit/render/spec-rendering/python
85-
- name: r-state-management
86-
path: cockpit/render/state-management/python
8723
steps:
8824
- uses: actions/checkout@v6.0.2
8925

26+
- uses: actions/setup-node@v6.3.0
27+
with:
28+
node-version: 22
29+
cache: npm
30+
31+
- run: npm ci
32+
9033
- uses: actions/setup-python@v5
9134
with:
9235
python-version: '3.12'
@@ -97,19 +40,16 @@ jobs:
9740
- name: Set up Docker Buildx
9841
uses: docker/setup-buildx-action@v3
9942

100-
- name: Write .env for deployment
101-
if: |
102-
github.event_name == 'workflow_dispatch' && (inputs.capability == '' || contains(matrix.path, inputs.capability))
103-
|| github.event_name == 'push'
104-
working-directory: ${{ matrix.path }}
43+
- name: Generate shared deployment manifest
44+
run: npx tsx scripts/generate-shared-deployment-config.ts
45+
46+
- name: Write .env for shared deployment
10547
run: |
106-
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" > .env
48+
mkdir -p deployments/shared-dev
49+
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" > deployments/shared-dev/.env
10750
108-
- name: Deploy ${{ matrix.name }}
109-
if: |
110-
github.event_name == 'workflow_dispatch' && (inputs.capability == '' || contains(matrix.path, inputs.capability))
111-
|| github.event_name == 'push'
112-
working-directory: ${{ matrix.path }}
113-
run: uv run --with langgraph-cli langgraph deploy --name ${{ matrix.name }} --no-wait
51+
- name: Deploy cockpit-dev
52+
working-directory: deployments/shared-dev
53+
run: uv run --with langgraph-cli langgraph deploy --config langgraph.json --name cockpit-dev --no-wait
11454
env:
11555
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ This file is for agents working in this repository. It is contributor-facing, no
4747
- The workspace is Nx-based. Prefer project-scoped commands over broad workspace runs unless the task actually needs broader verification.
4848
- Inspect `project.json`, `nx.json`, and existing scripts before inventing commands.
4949
- If you need Nx-specific syntax or behavior and it is not obvious from local config, verify it from current Nx docs rather than relying on memory.
50+
- The intended always-on LangSmith footprint is one shared cockpit dev deployment. Active capability keys in `deployment-urls.json` may all point at the same URL, and render demos stay local/static.
5051
- Respect generated and public-facing context files. If the task changes docs, API surface, positioning, or package guidance, check whether agent context or docs should be regenerated.
5152

5253
## Docs and Generated Context

apps/cockpit/scripts/capability-registry.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const capabilities: readonly Capability[] = [
2323
{ id: 'deployment-runtime', product: 'langgraph', topic: 'deployment-runtime', angularProject: 'cockpit-langgraph-deployment-runtime-angular', port: 4307, pythonDir: 'cockpit/langgraph/deployment-runtime/python', graphName: 'deployment-runtime' },
2424
{ id: 'planning', product: 'deep-agents', topic: 'planning', angularProject: 'cockpit-deep-agents-planning-angular', port: 4310, pythonDir: 'cockpit/deep-agents/planning/python', graphName: 'planning' },
2525
{ id: 'filesystem', product: 'deep-agents', topic: 'filesystem', angularProject: 'cockpit-deep-agents-filesystem-angular', port: 4311, pythonDir: 'cockpit/deep-agents/filesystem/python', graphName: 'filesystem' },
26-
{ id: 'da-subagents', product: 'deep-agents', topic: 'subagents', angularProject: 'cockpit-deep-agents-subagents-angular', port: 4312, pythonDir: 'cockpit/deep-agents/subagents/python', graphName: 'da-subagents' },
26+
{ id: 'da-subagents', product: 'deep-agents', topic: 'subagents', angularProject: 'cockpit-deep-agents-subagents-angular', port: 4312, pythonDir: 'cockpit/deep-agents/subagents/python', graphName: 'subagents' },
2727
{ id: 'da-memory', product: 'deep-agents', topic: 'memory', angularProject: 'cockpit-deep-agents-memory-angular', port: 4313, pythonDir: 'cockpit/deep-agents/memory/python', graphName: 'da-memory' },
2828
{ id: 'skills', product: 'deep-agents', topic: 'skills', angularProject: 'cockpit-deep-agents-skills-angular', port: 4314, pythonDir: 'cockpit/deep-agents/skills/python', graphName: 'skills' },
2929
{ id: 'sandboxes', product: 'deep-agents', topic: 'sandboxes', angularProject: 'cockpit-deep-agents-sandboxes-angular', port: 4315, pythonDir: 'cockpit/deep-agents/sandboxes/python', graphName: 'sandboxes' },
@@ -35,17 +35,17 @@ export const capabilities: readonly Capability[] = [
3535
{ id: 'repeat-loops', product: 'render', topic: 'repeat-loops', angularProject: 'cockpit-render-repeat-loops-angular', port: 4405, pythonDir: 'cockpit/render/repeat-loops/python', graphName: 'repeat-loops' },
3636
{ id: 'computed-functions', product: 'render', topic: 'computed-functions', angularProject: 'cockpit-render-computed-functions-angular', port: 4406, pythonDir: 'cockpit/render/computed-functions/python', graphName: 'computed-functions' },
3737
// Chat capabilities
38-
{ id: 'c-messages', product: 'chat', topic: 'messages', angularProject: 'cockpit-chat-messages-angular', port: 4501, pythonDir: 'cockpit/chat/messages/python', graphName: 'c-messages' },
39-
{ id: 'c-input', product: 'chat', topic: 'input', angularProject: 'cockpit-chat-input-angular', port: 4502, pythonDir: 'cockpit/chat/input/python', graphName: 'c-input' },
40-
{ id: 'c-interrupts', product: 'chat', topic: 'interrupts', angularProject: 'cockpit-chat-interrupts-angular', port: 4503, pythonDir: 'cockpit/chat/interrupts/python', graphName: 'c-interrupts' },
41-
{ id: 'c-tool-calls', product: 'chat', topic: 'tool-calls', angularProject: 'cockpit-chat-tool-calls-angular', port: 4504, pythonDir: 'cockpit/chat/tool-calls/python', graphName: 'c-tool-calls' },
42-
{ id: 'c-subagents', product: 'chat', topic: 'subagents', angularProject: 'cockpit-chat-subagents-angular', port: 4505, pythonDir: 'cockpit/chat/subagents/python', graphName: 'c-subagents' },
43-
{ id: 'c-threads', product: 'chat', topic: 'threads', angularProject: 'cockpit-chat-threads-angular', port: 4506, pythonDir: 'cockpit/chat/threads/python', graphName: 'c-threads' },
44-
{ id: 'c-timeline', product: 'chat', topic: 'timeline', angularProject: 'cockpit-chat-timeline-angular', port: 4507, pythonDir: 'cockpit/chat/timeline/python', graphName: 'c-timeline' },
45-
{ id: 'c-generative-ui', product: 'chat', topic: 'generative-ui', angularProject: 'cockpit-chat-generative-ui-angular', port: 4508, pythonDir: 'cockpit/chat/generative-ui/python', graphName: 'c-generative-ui' },
46-
{ id: 'c-debug', product: 'chat', topic: 'debug', angularProject: 'cockpit-chat-debug-angular', port: 4509, pythonDir: 'cockpit/chat/debug/python', graphName: 'c-debug' },
47-
{ id: 'c-theming', product: 'chat', topic: 'theming', angularProject: 'cockpit-chat-theming-angular', port: 4510, pythonDir: 'cockpit/chat/theming/python', graphName: 'c-theming' },
48-
{ id: 'c-a2ui', product: 'chat', topic: 'a2ui', angularProject: 'cockpit-chat-a2ui-angular', port: 4511, pythonDir: 'cockpit/chat/a2ui/python', graphName: 'c-a2ui' },
38+
{ id: 'c-messages', product: 'chat', topic: 'messages', angularProject: 'cockpit-chat-messages-angular', port: 4501, pythonDir: 'cockpit/langgraph/streaming/python', graphName: 'c-messages' },
39+
{ id: 'c-input', product: 'chat', topic: 'input', angularProject: 'cockpit-chat-input-angular', port: 4502, pythonDir: 'cockpit/langgraph/streaming/python', graphName: 'c-input' },
40+
{ id: 'c-interrupts', product: 'chat', topic: 'interrupts', angularProject: 'cockpit-chat-interrupts-angular', port: 4503, pythonDir: 'cockpit/langgraph/streaming/python', graphName: 'c-interrupts' },
41+
{ id: 'c-tool-calls', product: 'chat', topic: 'tool-calls', angularProject: 'cockpit-chat-tool-calls-angular', port: 4504, pythonDir: 'cockpit/langgraph/streaming/python', graphName: 'c-tool-calls' },
42+
{ id: 'c-subagents', product: 'chat', topic: 'subagents', angularProject: 'cockpit-chat-subagents-angular', port: 4505, pythonDir: 'cockpit/langgraph/streaming/python', graphName: 'c-subagents' },
43+
{ id: 'c-threads', product: 'chat', topic: 'threads', angularProject: 'cockpit-chat-threads-angular', port: 4506, pythonDir: 'cockpit/langgraph/streaming/python', graphName: 'c-threads' },
44+
{ id: 'c-timeline', product: 'chat', topic: 'timeline', angularProject: 'cockpit-chat-timeline-angular', port: 4507, pythonDir: 'cockpit/langgraph/streaming/python', graphName: 'c-timeline' },
45+
{ id: 'c-generative-ui', product: 'chat', topic: 'generative-ui', angularProject: 'cockpit-chat-generative-ui-angular', port: 4508, pythonDir: 'cockpit/langgraph/streaming/python', graphName: 'c-generative-ui' },
46+
{ id: 'c-debug', product: 'chat', topic: 'debug', angularProject: 'cockpit-chat-debug-angular', port: 4509, pythonDir: 'cockpit/langgraph/streaming/python', graphName: 'c-debug' },
47+
{ id: 'c-theming', product: 'chat', topic: 'theming', angularProject: 'cockpit-chat-theming-angular', port: 4510, pythonDir: 'cockpit/langgraph/streaming/python', graphName: 'c-theming' },
48+
{ id: 'c-a2ui', product: 'chat', topic: 'a2ui', angularProject: 'cockpit-chat-a2ui-angular', port: 4511, pythonDir: 'cockpit/langgraph/streaming/python', graphName: 'c-a2ui' },
4949
] as const;
5050

5151
export function findCapability(id: string): Capability | undefined {
Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,49 @@
1-
import { writeFileSync } from 'fs';
1+
import { readFileSync, writeFileSync } from 'fs';
22
import { resolve } from 'path';
33
import { capabilities } from './capability-registry';
44

5+
type LangGraphManifest = {
6+
graphs: Record<string, string>;
7+
};
8+
9+
const manifestCache = new Map<string, LangGraphManifest>();
10+
function readManifest(pythonDir: string): LangGraphManifest {
11+
const existing = manifestCache.get(pythonDir);
12+
if (existing) {
13+
return existing;
14+
}
15+
16+
const manifestPath = resolve(process.cwd(), pythonDir, 'langgraph.json');
17+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as LangGraphManifest;
18+
manifestCache.set(pythonDir, manifest);
19+
return manifest;
20+
}
21+
22+
function normalizeEntrypoint(pythonDir: string, graphName: string): string {
23+
const manifest = readManifest(pythonDir);
24+
const entrypoint = manifest.graphs[graphName]
25+
?? (() => {
26+
const manifestEntries = Object.entries(manifest.graphs);
27+
if (manifestEntries.length === 1) {
28+
return manifestEntries[0]?.[1];
29+
}
30+
return undefined;
31+
})();
32+
if (!entrypoint) {
33+
throw new Error(`Missing graph '${graphName}' in ${pythonDir}/langgraph.json`);
34+
}
35+
36+
return entrypoint.startsWith('./') ? entrypoint.slice(2) : entrypoint;
37+
}
38+
539
const graphs: Record<string, string> = {};
40+
const dependencies = new Set<string>();
641
for (const c of capabilities) {
7-
graphs[c.graphName] = `./${c.pythonDir}/src/graph.py:graph`;
42+
graphs[c.graphName] = `./${c.pythonDir}/${normalizeEntrypoint(c.pythonDir, c.graphName)}`;
43+
dependencies.add(`./${c.pythonDir}`);
844
}
945

10-
const config = { graphs, dependencies: capabilities.map((c) => `./${c.pythonDir}/pyproject.toml`), env: '.env' };
46+
const config = { graphs, dependencies: [...dependencies], env: '.env' };
1147
const out = resolve(process.cwd(), 'langgraph-combined.json');
1248
writeFileSync(out, JSON.stringify(config, null, 2) + '\n');
1349
console.log(`Generated ${out} with ${Object.keys(graphs).length} graphs`);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const environment = {
22
production: true,
33
langGraphApiUrl: '/api',
4-
a2uiAssistantId: 'a2ui_form',
4+
a2uiAssistantId: 'c-a2ui',
55
};
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const environment = {
22
production: true,
33
langGraphApiUrl: '/api',
4-
generativeUiAssistantId: 'generative_ui',
4+
generativeUiAssistantId: 'c-generative-ui',
55
};

cockpit/langgraph/streaming/python/langgraph.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"graphs": {
33
"streaming": "./src/graph.py:graph",
4-
"generative_ui": "./src/chat_graphs.py:generative_ui",
4+
"c-generative-ui": "./src/chat_graphs.py:generative_ui",
55
"c-messages": "./src/chat_graphs.py:c_messages",
66
"c-input": "./src/chat_graphs.py:c_input",
77
"c-debug": "./src/chat_graphs.py:c_debug",
@@ -11,7 +11,7 @@
1111
"c-timeline": "./src/chat_graphs.py:c_timeline",
1212
"c-tool-calls": "./src/chat_graphs.py:c_tool_calls",
1313
"c-subagents": "./src/chat_graphs.py:c_subagents",
14-
"a2ui_form": "./src/a2ui_graph.py:graph"
14+
"c-a2ui": "./src/a2ui_graph.py:graph"
1515
},
1616
"dependencies": [
1717
"."

0 commit comments

Comments
 (0)