Skip to content

Commit 6019967

Browse files
committed
Refactor output modes: rename *Output to *Mode for consistency AND handle
- Rename ToolOutput → ToolMode, NativeOutput → NativeMode, PromptedOutput → PromptMode
1 parent aacc01f commit 6019967

18 files changed

Lines changed: 681 additions & 125 deletions

.planning/implementation.md

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ The current structured output flow:
5959
```
6060

6161
4. **Various Output Modes**:
62-
- `ToolOutput`: Function calling for structured output
63-
- `NativeOutput`: Native model structured output capabilities
64-
- `PromptedOutput`: Prompting-based approach
62+
- `ToolMode`: Function calling for structured output
63+
- `NativeMode`: Native model structured output capabilities
64+
- `PromptMode`: Prompting-based approach
6565
- `TextOutput`: Post-processing of text output
6666
- `StructuredDict`: JSON schema validation
6767

@@ -114,14 +114,14 @@ class OutputSchema:
114114
description: Optional[str] = None
115115
):
116116
self.types = types if isinstance(types, list) else [types]
117-
self.mode = mode or ToolOutput() # Default to tool-based approach
117+
self.mode = mode or ToolMode() # Default to tool-based approach
118118
self.name = name
119119
self.description = description
120120
```
121121

122122
#### Output Mode Implementations (`modes.py`)
123123
```python
124-
class ToolOutput(OutputMode):
124+
class ToolMode(OutputMode):
125125
"""Use function calling for structured output (DEFAULT)
126126
127127
This is the most reliable approach across all model providers and ensures
@@ -135,11 +135,11 @@ class ToolOutput(OutputMode):
135135
"""Tool-based output is supported by all models that support function calling"""
136136
return True # All our models support function calling
137137

138-
class NativeOutput(OutputMode):
138+
class NativeMode(OutputMode):
139139
"""Use model's native structured output capabilities
140140
141141
Only use when explicitly requested and supported by the model.
142-
Falls back to ToolOutput if not supported.
142+
Falls back to ToolMode if not supported.
143143
"""
144144

145145
def get_tool_specs(self, output_types: list[Type[BaseModel]]) -> list[ToolSpec]:
@@ -150,7 +150,7 @@ class NativeOutput(OutputMode):
150150
"""Check if model supports native structured output"""
151151
return model.supports_native_structured_output()
152152

153-
class PromptedOutput(OutputMode):
153+
class PromptMode(OutputMode):
154154
"""Use prompting to guide output format
155155
156156
Only use when explicitly requested. Less reliable than tool-based approach
@@ -235,19 +235,19 @@ class Agent:
235235
if not output_type:
236236
return None
237237

238-
# Default to ToolOutput if no mode specified
239-
resolved_mode = output_mode or ToolOutput()
238+
# Default to ToolMode if no mode specified
239+
resolved_mode = output_mode or ToolMode()
240240

241241
# Validate mode is supported by current model
242242
if not resolved_mode.is_supported_by_model(self.model):
243-
if isinstance(resolved_mode, NativeOutput):
243+
if isinstance(resolved_mode, NativeMode):
244244
# Fallback to tool-based approach for native output
245245
logger.warning(
246246
f"Model {self.model.__class__.__name__} does not support native structured output. "
247247
"Falling back to tool-based approach."
248248
)
249-
resolved_mode = ToolOutput()
250-
elif isinstance(resolved_mode, PromptedOutput):
249+
resolved_mode = ToolMode()
250+
elif isinstance(resolved_mode, PromptMode):
251251
# This shouldn't happen as all models support prompting
252252
raise ValueError(f"Model {self.model.__class__.__name__} does not support prompting")
253253

@@ -326,23 +326,23 @@ class Model(abc.ABC):
326326
All providers default to tool-based approach unless explicitly overridden:
327327

328328
- **Bedrock**:
329-
- Default: ToolOutput (function calling)
330-
- Native: Not supported, falls back to ToolOutput
329+
- Default: ToolMode (function calling)
330+
- Native: Not supported, falls back to ToolMode
331331
- Prompted: Available when explicitly requested
332332

333333
- **OpenAI**:
334-
- Default: ToolOutput (function calling)
334+
- Default: ToolMode (function calling)
335335
- Native: Available when explicitly requested (structured_outputs=True)
336336
- Prompted: Available when explicitly requested
337337

338338
- **Anthropic**:
339-
- Default: ToolOutput (function calling)
340-
- Native: Not supported, falls back to ToolOutput
339+
- Default: ToolMode (function calling)
340+
- Native: Not supported, falls back to ToolMode
341341
- Prompted: Available when explicitly requested
342342

343343
- **Others (Ollama, LiteLLM, etc.)**:
344-
- Default: ToolOutput (function calling)
345-
- Native: Model-dependent, falls back to ToolOutput if not supported
344+
- Default: ToolMode (function calling)
345+
- Native: Model-dependent, falls back to ToolMode if not supported
346346
- Prompted: Available when explicitly requested
347347

348348
### 6. Backward Compatibility
@@ -369,23 +369,23 @@ def structured_output(self, output_model: Type[T], prompt: AgentInput = None) ->
369369

370370
#### Type-Safe Patterns
371371
```python
372-
# Agent-level output type (uses ToolOutput by default)
372+
# Agent-level output type (uses ToolMode by default)
373373
agent = Agent(model, output_type=UserProfile) # Uses function calling
374374
result = agent("Extract user info from: John Doe, age 30") # Returns AgentResult
375375
user = result.get_structured_output(UserProfile) # Extract UserProfile
376376

377-
# Runtime output type specification (uses ToolOutput by default)
377+
# Runtime output type specification (uses ToolMode by default)
378378
result = agent("Summarize this text", output_type=Summary) # Uses function calling
379379
summary = result.get_structured_output(Summary) # Extract Summary
380380

381381
# Explicit output mode specification
382-
precise_agent = Agent(model, output_type=Data, output_mode=NativeOutput()) # Uses native if supported
383-
fast_agent = Agent(model, output_type=Data, output_mode=PromptedOutput()) # Uses prompting
382+
precise_agent = Agent(model, output_type=Data, output_mode=NativeMode()) # Uses native if supported
383+
fast_agent = Agent(model, output_type=Data, output_mode=PromptMode()) # Uses prompting
384384

385385
# Runtime mode override
386-
result = agent("Extract data", output_type=Data, output_mode=NativeOutput()) # Uses native if supported
386+
result = agent("Extract data", output_type=Data, output_mode=NativeMode()) # Uses native if supported
387387

388-
# Multiple output types (still uses ToolOutput by default)
388+
# Multiple output types (still uses ToolMode by default)
389389
agent = Agent(model, output_type=[Person, Company]) # Creates multiple function tools
390390
result = agent("What is Apple Inc?") # Returns AgentResult
391391
entity = result.structured_output # Returns Person | Company instance
@@ -397,8 +397,8 @@ text = str(result) # Get text response
397397

398398
# Fallback behavior for unsupported modes
399399
try:
400-
# If model doesn't support native, automatically falls back to ToolOutput
401-
result = agent("Extract", output_type=Data, output_mode=NativeOutput())
400+
# If model doesn't support native, automatically falls back to ToolMode
401+
result = agent("Extract", output_type=Data, output_mode=NativeMode())
402402
except ValueError:
403403
# Only raises if something is fundamentally wrong
404404
pass
@@ -444,7 +444,7 @@ except ValueError:
444444
- Update all model providers for structured output support
445445

446446
2. **Provider-Specific Implementation**
447-
- All providers: Default to ToolOutput (function calling)
447+
- All providers: Default to ToolMode (function calling)
448448
- OpenAI: Add native structured output support when explicitly requested
449449
- Bedrock: Enhanced function calling with better schema handling
450450
- Anthropic: Enhanced function calling support
@@ -507,7 +507,7 @@ except ValueError:
507507
# Old approach
508508
user_data = agent.structured_output(UserModel, "Extract user info")
509509

510-
# New approach (uses ToolOutput by default - most reliable)
510+
# New approach (uses ToolMode by default - most reliable)
511511
result = agent("Extract user info", output_type=UserModel)
512512
user_data = result.get_structured_output(UserModel)
513513

@@ -521,19 +521,19 @@ user_data = result.structured_output
521521
text_result = agent("Generate summary")
522522
structured_result = agent.structured_output(Summary, "Generate summary")
523523

524-
# New: Unified interface (uses ToolOutput by default)
524+
# New: Unified interface (uses ToolMode by default)
525525
text_result = agent("Generate summary")
526526
structured_result = agent("Generate summary", output_type=Summary)
527527
summary = structured_result.get_structured_output(Summary)
528528

529-
# Or set default output type (uses ToolOutput by default)
529+
# Or set default output type (uses ToolMode by default)
530530
summary_agent = Agent(model, output_type=Summary)
531531
result = summary_agent("Generate summary")
532532
summary = result.get_structured_output(Summary)
533533

534534
# Explicit mode selection only when needed
535-
result = agent("Extract", output_type=Data, output_mode=NativeOutput())
536-
# Falls back to ToolOutput if model doesn't support native
535+
result = agent("Extract", output_type=Data, output_mode=NativeMode())
536+
# Falls back to ToolMode if model doesn't support native
537537
```
538538

539539
## Success Metrics

.planning/tasks.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This document breaks down the structured output implementation into discrete, ac
77
## Phase 1: Core Infrastructure ✅
88

99
### Task 1.1: Create Output Mode Base Classes ✅
10-
**Prompt:** Create the base output mode system in `src/strands/output/base.py`. Implement the abstract `OutputMode` class with methods for `get_tool_specs()`, `extract_result()`, and `is_supported_by_model()`. Also implement the `OutputSchema` container class that holds output types (Pydantic models only), mode, name, and description, with a default to `ToolOutput()`.
10+
**Prompt:** Create the base output mode system in `src/strands/output/base.py`. Implement the abstract `OutputMode` class with methods for `get_tool_specs()`, `extract_result()`, and `is_supported_by_model()`. Also implement the `OutputSchema` container class that holds output types (Pydantic models only), mode, name, and description, with a default to `ToolMode()`.
1111

1212
**Files to create/modify:**
1313
- `src/strands/output/__init__.py`
@@ -17,18 +17,18 @@ This document breaks down the structured output implementation into discrete, ac
1717
- Abstract `OutputMode` class with required methods ✅
1818
- `OutputSchema` class with proper initialization (Pydantic models only) ✅
1919
- Type hints and docstrings for all public APIs ✅
20-
- Default to `ToolOutput()` when no mode specified ✅
20+
- Default to `ToolMode()` when no mode specified ✅
2121

2222
### Task 1.2: Implement Output Mode Implementations ✅
23-
**Prompt:** Create the concrete output mode implementations in `src/strands/output/modes.py`. Implement `ToolOutput` (default), `NativeOutput`, and `PromptedOutput` classes. Each should implement the abstract methods from `OutputMode` and include model support detection. `ToolOutput` should always return `True` for `is_supported_by_model()`.
23+
**Prompt:** Create the concrete output mode implementations in `src/strands/output/modes.py`. Implement `ToolMode` (default), `NativeMode`, and `PromptMode` classes. Each should implement the abstract methods from `OutputMode` and include model support detection. `ToolMode` should always return `True` for `is_supported_by_model()`.
2424

2525
**Files to create/modify:**
2626
- `src/strands/output/modes.py`
2727

2828
**Acceptance criteria:**
29-
- `ToolOutput` class converts Pydantic models to tool specs ✅
30-
- `NativeOutput` class returns empty tool specs and checks model support ✅
31-
- `PromptedOutput` class with customizable template ✅
29+
- `ToolMode` class converts Pydantic models to tool specs ✅
30+
- `NativeMode` class returns empty tool specs and checks model support ✅
31+
- `PromptMode` class with customizable template ✅
3232
- All classes implement `is_supported_by_model()` correctly ✅
3333

3434
### Task 1.3: Create Output Registry System ✅
@@ -89,15 +89,15 @@ This document breaks down the structured output implementation into discrete, ac
8989
## Phase 3: Agent Interface Enhancement ✅
9090

9191
### Task 3.1: Enhance Agent Constructor ✅
92-
**Prompt:** Modify the `Agent` class constructor in `src/strands/agent/agent.py` to accept `output_type` (Pydantic models only) and `output_mode` parameters. Implement the `_resolve_output_schema()` method that defaults to `ToolOutput()` and includes model support validation with automatic fallback.
92+
**Prompt:** Modify the `Agent` class constructor in `src/strands/agent/agent.py` to accept `output_type` (Pydantic models only) and `output_mode` parameters. Implement the `_resolve_output_schema()` method that defaults to `ToolMode()` and includes model support validation with automatic fallback.
9393

9494
**Files to create/modify:**
9595
- `src/strands/agent/agent.py`
9696

9797
**Acceptance criteria:**
9898
- Constructor accepts `output_type` (Pydantic models only) and `output_mode` parameters ✅
9999
- `_resolve_output_schema()` method implemented ✅
100-
- Default to `ToolOutput()` when no mode specified ✅
100+
- Default to `ToolMode()` when no mode specified ✅
101101
- Model support validation with fallback logic ✅
102102
- Proper logging for fallback scenarios ✅
103103

@@ -115,7 +115,7 @@ This document breaks down the structured output implementation into discrete, ac
115115
- Backward compatibility for existing calls ✅
116116

117117
### Task 3.3: Implement Output Schema Resolution Logic ✅
118-
**Prompt:** Implement the complete output schema resolution logic in the `Agent` class. This includes handling runtime overrides, default schema application, model compatibility checking, and automatic fallback from unsupported modes to `ToolOutput()`.
118+
**Prompt:** Implement the complete output schema resolution logic in the `Agent` class. This includes handling runtime overrides, default schema application, model compatibility checking, and automatic fallback from unsupported modes to `ToolMode()`.
119119

120120
**Files to create/modify:**
121121
- `src/strands/agent/agent.py`
@@ -124,7 +124,7 @@ This document breaks down the structured output implementation into discrete, ac
124124
- Runtime output type override support ✅
125125
- Agent-level default schema application ✅
126126
- Model compatibility validation ✅
127-
- Automatic fallback to `ToolOutput()`
127+
- Automatic fallback to `ToolMode()`
128128
- Comprehensive error handling and logging ✅
129129

130130
## Phase 4: Event Loop Integration ✅

docs/migration_guide.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ user = result.get_structured_output(UserProfile)
6969

7070
**New Feature:**
7171
```python
72-
from strands import NativeOutput, ToolOutput
72+
from strands import NativeMode, ToolMode
7373

7474
# Use native structured output when available
75-
agent = Agent(output_type=UserProfile, output_mode=NativeOutput())
75+
agent = Agent(output_type=UserProfile, output_mode=NativeMode())
7676

7777
# Or explicitly use tool-based approach
78-
agent = Agent(output_type=UserProfile, output_mode=ToolOutput())
78+
agent = Agent(output_type=UserProfile, output_mode=ToolMode())
7979
```
8080

8181
## Common Migration Patterns
@@ -170,14 +170,14 @@ with warnings.catch_warnings():
170170
### 1. Output Modes
171171

172172
```python
173-
from strands import NativeOutput, PromptedOutput
173+
from strands import NativeMode, PromptMode
174174

175175
# Use model's native structured output
176-
agent = Agent(output_type=UserProfile, output_mode=NativeOutput())
176+
agent = Agent(output_type=UserProfile, output_mode=NativeMode())
177177

178178
# Use custom prompt template
179179
template = "Extract information: {prompt}\nFormat as JSON:"
180-
agent = Agent(output_type=UserProfile, output_mode=PromptedOutput(template=template))
180+
agent = Agent(output_type=UserProfile, output_mode=PromptMode(template=template))
181181
```
182182

183183
### 2. Model Capability Detection
@@ -195,11 +195,11 @@ bedrock_agent = Agent(model=BedrockModel(model_id="claude-3"), output_type=UserP
195195
### 3. Multiple Output Types
196196

197197
```python
198-
from strands import OutputSchema, ToolOutput
198+
from strands import OutputSchema, ToolMode
199199

200200
schema = OutputSchema(
201201
types=[UserProfile, TaskInfo],
202-
mode=ToolOutput(),
202+
mode=ToolMode(),
203203
description="Can output user profile or task information"
204204
)
205205

0 commit comments

Comments
 (0)