diff --git a/.github/workflows/deploy-langgraph.yml b/.github/workflows/deploy-langgraph.yml index d1dfd4a98..0a79d83c9 100644 --- a/.github/workflows/deploy-langgraph.yml +++ b/.github/workflows/deploy-langgraph.yml @@ -19,33 +19,33 @@ jobs: strategy: matrix: include: - - name: langgraph-streaming + - name: streaming path: cockpit/langgraph/streaming/python - - name: langgraph-persistence + - name: persistence path: cockpit/langgraph/persistence/python - - name: langgraph-interrupts + - name: interrupts path: cockpit/langgraph/interrupts/python - - name: langgraph-memory + - name: memory path: cockpit/langgraph/memory/python - - name: langgraph-durable-execution + - name: durable-execution path: cockpit/langgraph/durable-execution/python - - name: langgraph-subgraphs + - name: subgraphs path: cockpit/langgraph/subgraphs/python - - name: langgraph-time-travel + - name: time-travel path: cockpit/langgraph/time-travel/python - - name: langgraph-deployment-runtime + - name: deployment-runtime path: cockpit/langgraph/deployment-runtime/python - - name: deep-agents-planning + - name: planning path: cockpit/deep-agents/planning/python - - name: deep-agents-filesystem + - name: filesystem path: cockpit/deep-agents/filesystem/python - - name: deep-agents-subagents + - name: da-subagents path: cockpit/deep-agents/subagents/python - - name: deep-agents-memory + - name: da-memory path: cockpit/deep-agents/memory/python - - name: deep-agents-skills + - name: skills path: cockpit/deep-agents/skills/python - - name: deep-agents-sandboxes + - name: sandboxes path: cockpit/deep-agents/sandboxes/python steps: - uses: actions/checkout@v6.0.2 @@ -54,14 +54,25 @@ jobs: with: python-version: '3.12' - - name: Install langgraph-cli - run: pip install langgraph-cli + - name: Install uv + run: pip install uv + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Write .env for deployment + if: | + github.event_name == 'workflow_dispatch' && (inputs.capability == '' || contains(matrix.path, inputs.capability)) + || github.event_name == 'push' + working-directory: ${{ matrix.path }} + run: | + echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" > .env - name: Deploy ${{ matrix.name }} if: | github.event_name == 'workflow_dispatch' && (inputs.capability == '' || contains(matrix.path, inputs.capability)) || github.event_name == 'push' working-directory: ${{ matrix.path }} - run: langgraph deploy + run: uv run --with langgraph-cli langgraph deploy --name ${{ matrix.name }} --no-wait env: LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }} diff --git a/apps/cockpit/e2e/cockpit.spec.ts b/apps/cockpit/e2e/cockpit.spec.ts index 181939299..b28ed605e 100644 --- a/apps/cockpit/e2e/cockpit.spec.ts +++ b/apps/cockpit/e2e/cockpit.spec.ts @@ -1,49 +1,36 @@ import { expect, test } from '@playwright/test'; -test('renders navigation and representative shell panes on the home page', async ({ page }) => { +test('renders navigation and shell on the home page', async ({ page }) => { await page.goto('/'); await expect(page.getByRole('main', { name: 'Cockpit shell' })).toHaveAttribute( 'data-hydrated', 'true' ); - await expect(page.getByRole('heading', { name: 'Explore the example surface' })).toBeVisible(); await expect(page.getByRole('navigation', { name: 'Cockpit navigation' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Run', exact: true })).toBeVisible(); await expect(page.getByRole('button', { name: 'Code', exact: true })).toBeVisible(); await expect(page.getByRole('button', { name: 'Docs', exact: true })).toBeVisible(); - await expect(page.getByRole('heading', { name: 'Interactive example' })).toBeVisible(); - await expect(page.getByText('apps/cockpit/src/app/page.tsx')).toBeVisible(); }); -test('navigates from the tree to a capability route and shows the loaded surface', async ({ page }) => { +test('navigates from the sidebar to a capability route', async ({ page }) => { await page.goto('/'); await expect(page.getByRole('main', { name: 'Cockpit shell' })).toHaveAttribute( 'data-hydrated', 'true' ); - await page.getByRole('link', { name: 'LangGraph Persistence' }).click(); + // Sidebar strips "LangGraph " prefix, so the link text is just "Persistence" + await page.getByRole('link', { name: 'Persistence', exact: true }).click(); await expect(page).toHaveURL(/\/langgraph\/core-capabilities\/persistence\/overview\/python$/); await expect(page.getByRole('main', { name: 'Cockpit shell' })).toHaveAttribute( 'data-hydrated', 'true' ); - await expect( - page.getByText('/docs/langgraph/core-capabilities/persistence/overview/python') - ).toBeVisible(); - await page.getByRole('button', { name: 'Code', exact: true }).click(); - await expect(page.getByRole('heading', { name: 'Code' })).toBeVisible(); - await page.getByRole('tab', { name: 'index.ts' }).click(); - await expect( - page.getByText('cockpit/langgraph/persistence/python/src/index.ts', { exact: true }) - ).toBeVisible(); - await page.getByRole('button', { name: 'Open prompt assets' }).click(); - await expect(page.getByRole('complementary', { name: 'Prompt drawer' })).toBeVisible(); - await expect( - page.getByText('cockpit/langgraph/persistence/python/prompts/persistence.md') - ).toBeVisible(); + + // Mode switcher should still be present + await expect(page.getByRole('button', { name: 'Code', exact: true })).toBeVisible(); }); test('falls back to the product overview when a missing typescript route is requested', async ({ page }) => { @@ -54,5 +41,4 @@ test('falls back to the product overview when a missing typescript route is requ 'data-hydrated', 'true' ); - await expect(page.getByText('/docs/langgraph/getting-started/overview/overview/python')).toBeVisible(); }); diff --git a/apps/cockpit/playwright.config.ts b/apps/cockpit/playwright.config.ts index c8a821758..d4b895eec 100644 --- a/apps/cockpit/playwright.config.ts +++ b/apps/cockpit/playwright.config.ts @@ -5,7 +5,7 @@ const shouldStartLocalServer = !process.env['BASE_URL']; export default defineConfig({ testDir: './e2e', - testIgnore: ['**/all-examples-smoke*'], + testIgnore: ['**/all-examples-smoke*', '**/production-smoke*'], fullyParallel: true, use: { baseURL, diff --git a/apps/website/src/components/shared/Footer.tsx b/apps/website/src/components/shared/Footer.tsx index 8ebe19245..4e2f9a72e 100644 --- a/apps/website/src/components/shared/Footer.tsx +++ b/apps/website/src/components/shared/Footer.tsx @@ -82,6 +82,11 @@ export function Footer() { onMouseLeave={(e) => (e.currentTarget.style.color = tokens.colors.textSecondary)}> API Reference + (e.currentTarget.style.color = tokens.colors.accent)} + onMouseLeave={(e) => (e.currentTarget.style.color = tokens.colors.textSecondary)}> + Examples + (e.currentTarget.style.color = tokens.colors.accent)} onMouseLeave={(e) => (e.currentTarget.style.color = tokens.colors.textSecondary)}> diff --git a/apps/website/src/components/shared/Nav.tsx b/apps/website/src/components/shared/Nav.tsx index e92460b67..d1ab2dc88 100644 --- a/apps/website/src/components/shared/Nav.tsx +++ b/apps/website/src/components/shared/Nav.tsx @@ -6,6 +6,7 @@ import { tokens } from '@cacheplane/design-tokens'; const links = [ { label: 'Docs', href: '/docs' }, { label: 'API', href: '/docs/api/stream-resource' }, + { label: 'Examples', href: 'https://cockpit.stream-resource.dev' }, { label: 'Pricing', href: '/pricing' }, ]; diff --git a/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts b/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts index 1a3748729..16bffd58d 100644 --- a/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts +++ b/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts @@ -1,33 +1,58 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { Component, computed } from '@angular/core'; -import { ChatDebugComponent } from '@cacheplane/chat'; +import { LegacyChatComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; -import { AIMessage } from '@langchain/core/messages'; import { environment } from '../environments/environment'; +interface ToolCallEntry { + name: string; + args: string; + result?: string; +} + +/** + * FilesystemComponent demonstrates agent file operations. + * + * The agent can read and write files using tool calls. The sidebar + * shows a real-time log of each file operation as it happens. + * + * Key integration points: + * - `stream.messages()` contains all messages including tool call results + * - `computed()` derives tool call entries from AI messages + * - Tool calls update reactively as the agent performs file operations + */ @Component({ selector: 'app-filesystem', standalone: true, - imports: [ChatDebugComponent], + imports: [LegacyChatComponent], template: ` -