Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2b00730
refactor(chat): caret span has no glyph (CSS will paint the dot)
blove May 3, 2026
0a337f8
feat(chat): streaming caret as a glowing dot (matches welcome beacon)
blove May 3, 2026
5658f4d
refactor(chat): drop chat-welcome [chatWelcomeSubtitle] slot
blove May 3, 2026
a69859f
refactor(chat): remove chat-welcome subtitle styles + tighten gap def…
blove May 3, 2026
4278db8
test(chat): chat-welcome no longer has a subtitle
blove May 3, 2026
b95cc65
feat(chat): add --ngaf-chat-edge-pad token (drives symmetric top/bott…
blove May 3, 2026
31fa468
feat(chat): pill input + circle send/stop + content-width + edge-pad
blove May 3, 2026
fc1b564
feat(chat): symmetric top/bottom spacing via --ngaf-chat-edge-pad
blove May 3, 2026
79cad29
feat(chat): chat-input [chatInputModelSelect] slot in pill controls
blove May 3, 2026
245a03c
test(chat): chat-input pill + circle send + model-select slot
blove May 3, 2026
4ff223a
feat(chat): chat-select styles
blove May 3, 2026
be84afc
test(chat): failing specs for chat-select primitive
blove May 3, 2026
3c1c4ee
feat(chat): chat-select primitive (ghosted, fully rounded, popover)
blove May 3, 2026
593f1b1
feat(chat): export ChatSelectComponent + ChatSelectOption
blove May 3, 2026
5ebf8db
fix(langgraph): bypass throttle on length-growth emissions of message…
blove May 3, 2026
3c065a0
test(chat): regression — first message after submit is data-role=user
blove May 3, 2026
c4edf6c
docs(website): chat-select component reference
blove May 3, 2026
ec24304
docs(website): document [chatInputModelSelect] slot in chat-input
blove May 3, 2026
14b5d71
fix(chat): chat-select listbox a11y + bump 0.0.16 → 0.0.17
blove May 3, 2026
bf3b0a9
fix(langgraph): stream complex-content correctly + dedupe assistant b…
blove May 3, 2026
3a2b51a
fix(chat): render markdown with full GFM coverage + visible CommonMar…
blove May 3, 2026
66df5c2
feat(chat): first-class model picker on <chat> + restyle welcome sugg…
blove May 3, 2026
840163d
chore: bump cockpit demos + superpowers docs from gpt-4o-mini to gpt-…
blove May 3, 2026
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
12 changes: 12 additions & 0 deletions apps/website/content/docs/chat/components/chat-input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ function submitMessage(

The function calls `ref.submit({ message: trimmed })` under the hood.

## Slots

### `[chatInputModelSelect]`

Projects content into the controls row of the input pill, between `[chatInputTrailing]` and the send button. Designed for `<chat-select>` (a model picker), but accepts any element.

```html
<chat-input [agent]="agent">
<chat-select chatInputModelSelect [options]="opts" [(value)]="selected" />
</chat-input>
```

## Styling

The component renders a `<form>` containing a `<textarea>` and a `<button>`. It uses the following CSS custom properties from the chat theme:
Expand Down
122 changes: 122 additions & 0 deletions apps/website/content/docs/chat/components/chat-select.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# ChatSelectComponent

`ChatSelectComponent` is a generic single-select dropdown. It renders a ghosted, fully rounded trigger and a popover menu. Designed to slot into the chat input pill (via `[chatInputModelSelect]`) for a model picker, but usable anywhere.

**Selector:** `chat-select`

**Import:**

```typescript
import { ChatSelectComponent, type ChatSelectOption } from '@ngaf/chat';
```

## Basic Usage

Project into the chat input pill so the select appears between the trailing slot and the send button:

```html
<chat [agent]="agent">
<chat-select
chatInputModelSelect
[options]="models()"
[(value)]="selectedModel"
placeholder="Choose a model"
/>
</chat>
```

Standalone usage (anywhere):

```html
<chat-select
[options]="opts"
[(value)]="selected"
placeholder="Pick one"
/>
```

## API

### Inputs

| Input | Type | Default | Description |
|---------------|--------------------------------------|--------------|-------------|
| `options` | `readonly ChatSelectOption[]` | **Required** | Items to display in the menu. |
| `value` | `string` (two-way, `model()`) | `''` | The currently selected option's `value`. |
| `placeholder` | `string` | `'Select'` | Label rendered in the trigger when no option matches `value`. |
| `disabled` | `boolean` | `false` | Disables the trigger. |
| `menuLabel` | `string \| undefined` | placeholder | aria-label for the popover. |

### `ChatSelectOption`

```typescript
interface ChatSelectOption {
value: string;
label: string;
disabled?: boolean;
}
```

### Two-way binding

Use `[(value)]` to bind a writable signal:

```html
<chat-select [options]="opts" [(value)]="selected" />
```

Or bind one-way + listen for changes:

```html
<chat-select
[options]="opts"
[value]="selected()"
(valueChange)="selected.set($event)"
/>
```

## Behavior

### Open / Close

- **Open**: click the trigger, or press `Enter`/`Space`/`↓` while it has focus.
- **Close**: click an option, click outside, or press `Esc`.

### Keyboard

| Key | Behavior |
|----------------|-------------------------------------------------|
| `Enter`/`Space`/`↓` on trigger | Opens the menu, focuses first option |
| `↑` / `↓` in menu | Moves focus to prev/next non-disabled option |
| `Enter` / `Space` on option | Selects, closes, returns focus to trigger |
| `Esc` | Closes, returns focus to trigger |

### Disabled options

A `ChatSelectOption` with `disabled: true` is rendered, skipped during keyboard navigation, and not selectable by click.

### Menu position

The menu opens UP (anchored above the trigger) so it lands above the chat input when the select sits inside the input pill. For standalone usage at the top of a page the menu may overflow the viewport upward — wrap in a positioned container if needed.

## Theming

Inherits from chat tokens. Override on `:host` or any ancestor:

```css
chat-select {
--ngaf-chat-text-muted: hsl(0 0% 50%);
--ngaf-chat-surface-alt: hsl(0 0% 96%);
--ngaf-chat-shadow-lg: 0 8px 24px rgba(0,0,0,.12);
}
```

## Public API

```typescript
import { ChatSelectComponent, type ChatSelectOption } from '@ngaf/chat';
```

## See also

- `ChatInputComponent` — the chat input pill that hosts the `[chatInputModelSelect]` slot.
2 changes: 1 addition & 1 deletion cockpit/chat/generative-ui/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

_PROMPT = (Path(__file__).parent.parent / "prompts" / "dashboard.md").read_text()

_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0, streaming=True)
_llm = ChatOpenAI(model="gpt-5-mini", temperature=0, streaming=True)
_llm_with_tools = _llm.bind_tools(ALL_TOOLS)


Expand Down
2 changes: 1 addition & 1 deletion cockpit/deep-agents/filesystem/python/docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def write_file(path: str, content: str) -> str:
return f"Successfully wrote {len(content)} bytes to {path}"

# Bind tools to the LLM
llm = ChatOpenAI(model="gpt-4o-mini").bind_tools([read_file, write_file])
llm = ChatOpenAI(model="gpt-5-mini").bind_tools([read_file, write_file])
```

The agent node invokes the LLM, which may emit tool calls. A conditional edge routes to the `ToolNode` when tool calls are present, then loops back to the agent. The frontend sees each tool call in `stream.messages()`.
Expand Down
2 changes: 1 addition & 1 deletion cockpit/deep-agents/filesystem/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class FilesystemState(TypedDict):

def build_filesystem_graph():
tools = [read_file, write_file]
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True).bind_tools(tools)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True).bind_tools(tools)

async def agent(state: FilesystemState) -> dict:
"""Run the agent — may emit tool calls."""
Expand Down
2 changes: 1 addition & 1 deletion cockpit/deep-agents/memory/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class MemoryState(TypedDict):


def build_memory_graph():
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

async def generate(state: MemoryState) -> dict:
"""Generate a response using remembered facts in the system prompt."""
Expand Down
2 changes: 1 addition & 1 deletion cockpit/deep-agents/planning/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class PlanningState(TypedDict):


def build_planning_graph():
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

async def create_plan(state: PlanningState) -> dict:
"""Decompose the task into ordered steps."""
Expand Down
2 changes: 1 addition & 1 deletion cockpit/deep-agents/sandboxes/python/docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def run_code(code: str) -> str:
"exit_status": 0,
})

llm = ChatOpenAI(model="gpt-4o-mini").bind_tools([run_code])
llm = ChatOpenAI(model="gpt-5-mini").bind_tools([run_code])
tool_node = ToolNode([run_code])
```

Expand Down
2 changes: 1 addition & 1 deletion cockpit/deep-agents/sandboxes/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def run_code(code: str) -> str:

def build_sandboxes_graph():
tools = [run_code]
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True).bind_tools(tools)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True).bind_tools(tools)
tool_node = ToolNode(tools)

async def agent(state: SandboxesState) -> dict:
Expand Down
2 changes: 1 addition & 1 deletion cockpit/deep-agents/skills/python/docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def summarize(text: str) -> str:
return sentences[0] + "." if sentences else "No content."

# Bind all tools to the LLM
llm = ChatOpenAI(model="gpt-4o-mini").bind_tools([calculator, word_count, summarize])
llm = ChatOpenAI(model="gpt-5-mini").bind_tools([calculator, word_count, summarize])
```

The agent selects which skill to call based on the user's request. `ToolNode` dispatches the call and returns the result as a `ToolMessage`.
Expand Down
2 changes: 1 addition & 1 deletion cockpit/deep-agents/skills/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def summarize(text: str) -> str:

def build_skills_graph():
tools = [calculator, word_count, summarize]
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True).bind_tools(tools)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True).bind_tools(tools)
tool_node = ToolNode(tools)

async def agent(state: SkillsState) -> dict:
Expand Down
8 changes: 4 additions & 4 deletions cockpit/deep-agents/subagents/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ class SubagentsState(TypedDict):


def build_subagents_graph():
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

@tool
async def research_agent(topic: str) -> str:
"""Spawn a research subagent to gather information on a topic."""
research_llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
research_llm = ChatOpenAI(model="gpt-5-mini", streaming=True)
response = await research_llm.ainvoke([
SystemMessage(content="You are a research specialist. Provide concise, factual information."),
{"role": "human", "content": f"Research this topic and provide key facts: {topic}"},
Expand All @@ -38,7 +38,7 @@ async def research_agent(topic: str) -> str:
@tool
async def analysis_agent(content: str) -> str:
"""Spawn an analysis subagent to analyze and synthesize information."""
analysis_llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
analysis_llm = ChatOpenAI(model="gpt-5-mini", streaming=True)
response = await analysis_llm.ainvoke([
SystemMessage(content="You are an analysis specialist. Identify patterns, draw insights, and synthesize information clearly."),
{"role": "human", "content": f"Analyze this content and provide key insights: {content}"},
Expand All @@ -48,7 +48,7 @@ async def analysis_agent(content: str) -> str:
@tool
async def summary_agent(findings: str) -> str:
"""Spawn a summary subagent to produce a final coherent response."""
summary_llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
summary_llm = ChatOpenAI(model="gpt-5-mini", streaming=True)
response = await summary_llm.ainvoke([
SystemMessage(content="You are a summarization specialist. Produce clear, well-structured summaries."),
{"role": "human", "content": f"Summarize these findings into a concise final answer: {findings}"},
Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/deployment-runtime/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def build_deployment_runtime_graph():
LangGraph Cloud. The assistantId in the Angular component must match
the graph key in langgraph.json.
"""
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

async def generate(state: MessagesState) -> dict:
"""Generate a response using the full message history."""
Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/durable-execution/python/docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class DurableState(TypedDict):
checkpointer = MemorySaver()

def build_durable_execution_graph():
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

async def analyze(state): ... # Node 1
async def plan(state): ... # Node 2
Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/durable-execution/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def build_durable_execution_graph():
and checkpoints state after each one. This means any node failure
only requires replaying from the previous checkpoint, not the start.
"""
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)
system_prompt = (PROMPTS_DIR / "durable-execution.md").read_text()

async def analyze(state: DurableState) -> dict:
Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/interrupts/python/docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ from langgraph.types import interrupt
checkpointer = MemorySaver()

def build_interrupts_graph():
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

async def generate(state: MessagesState) -> dict:
response = await llm.ainvoke(state["messages"])
Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/memory/python/docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class MemoryState(TypedDict):
checkpointer = MemorySaver()

def build_memory_graph():
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

async def generate(state: MemoryState) -> dict:
memory = state.get("memory", {})
Expand Down
4 changes: 2 additions & 2 deletions cockpit/langgraph/memory/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ def build_memory_graph():
This ensures every response is followed by a memory extraction pass,
keeping the agent's knowledge up to date without blocking the reply.
"""
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
extractor_llm = ChatOpenAI(model="gpt-4o-mini", streaming=False)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)
extractor_llm = ChatOpenAI(model="gpt-5-mini", streaming=False)

async def generate(state: MemoryState) -> dict:
"""Generate a response using current messages and known memory."""
Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/persistence/python/docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()

def build_persistence_graph():
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

async def generate(state: MessagesState) -> dict:
response = await llm.ainvoke(state["messages"])
Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/streaming/python/docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI

def build_streaming_graph():
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

async def generate(state):
response = await llm.ainvoke(state["messages"])
Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/streaming/python/src/dashboard_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

_PROMPT = (Path(__file__).parent.parent / "prompts" / "dashboard.md").read_text()

_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0, streaming=True)
_llm = ChatOpenAI(model="gpt-5-mini", temperature=0, streaming=True)
_llm_with_tools = _llm.bind_tools(ALL_TOOLS)


Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/subgraphs/python/docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ The backend uses a parent orchestrator that delegates to a compiled child subgra
from langgraph.graph import StateGraph, MessagesState, END
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

# Child: research subgraph
async def research_node(state: MessagesState) -> dict:
Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/subgraphs/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def build_subgraphs_graph():
The parent orchestrator decides when to delegate to the research subgraph.
The research subgraph runs independently and returns its results to the parent.
"""
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

# ── Child: research subgraph ──────────────────────────────────────────────

Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/time-travel/python/docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()

def build_time_travel_graph():
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

async def generate(state: MessagesState) -> dict:
response = await llm.ainvoke(state["messages"])
Expand Down
2 changes: 1 addition & 1 deletion cockpit/langgraph/time-travel/python/src/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def build_time_travel_graph():
producing a history of ThreadState objects that the client can replay or
branch from using checkpoint IDs.
"""
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
llm = ChatOpenAI(model="gpt-5-mini", streaming=True)

async def generate(state: MessagesState) -> dict:
"""Generate a response, checkpointed for time travel."""
Expand Down
4 changes: 2 additions & 2 deletions docs/superpowers/plans/2026-03-19-langsmith-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Website (Vercel)
└─ NEXT_PUBLIC_LANGGRAPH_URL
└─ LangGraph Cloud (LangSmith)
└─ chat_agent graph (Python)
└─ ChatOpenAI (gpt-4o-mini)
└─ ChatOpenAI (gpt-5-mini)
```

---
Expand Down Expand Up @@ -141,7 +141,7 @@ From the LangSmith dashboard → Deployments → your deployment → Environment
| Variable | Value |
|---|---|
| `OPENAI_API_KEY` | Your OpenAI key |
| `OPENAI_MODEL` | `gpt-4o-mini` (default) or preferred model |
| `OPENAI_MODEL` | `gpt-5-mini` (default) or preferred model |
| `LANGSMITH_TRACING` | `true` (optional, for production tracing) |
| `LANGSMITH_PROJECT` | `angular-example` |

Expand Down
Loading