From 350beb01898aa7ba063f21adb2def8109c8024a8 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Fri, 8 May 2026 13:35:04 -0700 Subject: [PATCH 1/6] feat(examples-chat-python): honor state.reasoning_effort --- examples/chat/python/src/graph.py | 13 +++++++++---- examples/chat/python/tests/test_graph_smoke.py | 4 +++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/examples/chat/python/src/graph.py b/examples/chat/python/src/graph.py index 7abda520b..784d0b8e8 100644 --- a/examples/chat/python/src/graph.py +++ b/examples/chat/python/src/graph.py @@ -3,6 +3,10 @@ State the client may send via the LangGraph ``submit``'s ``state`` field: - ``model`` — OpenAI model name. Default: ``gpt-5-mini``. + - ``reasoning_effort`` — 'minimal' | 'low' | 'medium' | 'high'. + Default: 'minimal' so first-token latency + stays low. Demos surface this as a palette + dropdown so users can dial in visible reasoning. The graph is intentionally minimal: ``__start__ → generate → __end__``. This is the surface the demo's regenerate path exercises and the @@ -35,16 +39,17 @@ def _is_reasoning_model(name: str) -> bool: class State(TypedDict): messages: Annotated[list, add_messages] model: Optional[str] + reasoning_effort: Optional[str] async def generate(state: State) -> dict: model_name = state.get("model") or "gpt-5-mini" kwargs = {"model": model_name, "streaming": True} if _is_reasoning_model(model_name): - # Force minimal effort so first-token latency stays low and - # streaming is visible out of the box. Reasoning-effort tuning - # is deferred to the reasoning-phase demo. - kwargs["reasoning"] = {"effort": "minimal"} + # Honor the client's effort selection when present; default to + # 'minimal' so first-token latency stays low for unconfigured callers. + effort = state.get("reasoning_effort") or "minimal" + kwargs["reasoning"] = {"effort": effort} llm = ChatOpenAI(**kwargs) messages = [SystemMessage(content=SYSTEM_PROMPT)] + state["messages"] response = await llm.ainvoke(messages) diff --git a/examples/chat/python/tests/test_graph_smoke.py b/examples/chat/python/tests/test_graph_smoke.py index 4b346500d..128ed48ad 100644 --- a/examples/chat/python/tests/test_graph_smoke.py +++ b/examples/chat/python/tests/test_graph_smoke.py @@ -15,8 +15,10 @@ def test_graph_imports(): @pytest.mark.smoke -def test_state_shape_includes_messages_and_model(): +def test_state_shape_includes_required_channels(): from src.graph import State annotations = State.__annotations__ assert "messages" in annotations, "State must have a `messages` channel" assert "model" in annotations, "State must have a `model` channel" + assert "reasoning_effort" in annotations, \ + "State must have a `reasoning_effort` channel (Phase 2A)" From 88be90ca7a015581bed2f4d18545afd48952d1e1 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Fri, 8 May 2026 13:37:02 -0700 Subject: [PATCH 2/6] feat(examples-chat-angular): PaletteState gains effort key --- .../src/app/shell/palette-persistence.service.spec.ts | 7 +++++++ .../angular/src/app/shell/palette-persistence.service.ts | 1 + 2 files changed, 8 insertions(+) diff --git a/examples/chat/angular/src/app/shell/palette-persistence.service.spec.ts b/examples/chat/angular/src/app/shell/palette-persistence.service.spec.ts index 4fb5ea684..940925ee0 100644 --- a/examples/chat/angular/src/app/shell/palette-persistence.service.spec.ts +++ b/examples/chat/angular/src/app/shell/palette-persistence.service.spec.ts @@ -12,6 +12,7 @@ describe('PalettePersistence', () => { it('returns null when nothing is stored', () => { const svc = TestBed.runInInjectionContext(() => new PalettePersistence()); expect(svc.read('model')).toBeNull(); + expect(svc.read('effort')).toBeNull(); expect(svc.read('debug')).toBeNull(); expect(svc.read('threadId')).toBeNull(); expect(svc.read('collapsed')).toBeNull(); @@ -23,6 +24,12 @@ describe('PalettePersistence', () => { expect(svc.read('model')).toBe('gpt-5-mini'); }); + it('round-trips effort', () => { + const svc = TestBed.runInInjectionContext(() => new PalettePersistence()); + svc.write('effort', 'high'); + expect(svc.read('effort')).toBe('high'); + }); + it('round-trips a boolean value', () => { const svc = TestBed.runInInjectionContext(() => new PalettePersistence()); svc.write('debug', true); diff --git a/examples/chat/angular/src/app/shell/palette-persistence.service.ts b/examples/chat/angular/src/app/shell/palette-persistence.service.ts index b3e69c398..3aea291aa 100644 --- a/examples/chat/angular/src/app/shell/palette-persistence.service.ts +++ b/examples/chat/angular/src/app/shell/palette-persistence.service.ts @@ -5,6 +5,7 @@ const KEY = 'ngaf-chat-demo:palette'; interface PaletteState { model?: string | null; + effort?: string | null; debug?: boolean | null; threadId?: string | null; collapsed?: boolean | null; From 8b841f1f95d2d2156ec6e724d71df3a3a595ea4e Mon Sep 17 00:00:00 2001 From: Brian Love Date: Fri, 8 May 2026 13:39:04 -0700 Subject: [PATCH 3/6] feat(examples-chat-angular): control-palette gains Effort dropdown --- .../angular/src/app/shell/control-palette.component.html | 9 +++++++++ .../angular/src/app/shell/control-palette.component.ts | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/examples/chat/angular/src/app/shell/control-palette.component.html b/examples/chat/angular/src/app/shell/control-palette.component.html index 62fdcf0ee..961bc0952 100644 --- a/examples/chat/angular/src/app/shell/control-palette.component.html +++ b/examples/chat/angular/src/app/shell/control-palette.component.html @@ -42,6 +42,15 @@ + +