Skip to content

PR 12: Documentation and examples - #13

Open
subtleGradient wants to merge 3 commits into
tom/call-model-11-client-integrationfrom
tom/call-model-12-docs-examples
Open

PR 12: Documentation and examples#13
subtleGradient wants to merge 3 commits into
tom/call-model-11-client-integrationfrom
tom/call-model-12-docs-examples

Conversation

@subtleGradient

Copy link
Copy Markdown
Owner

Summary

  • Add comprehensive QUICKSTART.md guide
  • Create 4 runnable example scripts
  • Update README.md with quick start section
  • Fix API integration issues for Pydantic events
  • Remove emojis and hyperbolic language for professional tone

Builds on #12.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds comprehensive documentation and examples for the call_model() API while fixing integration issues for Pydantic events from the OpenRouter API.

Key changes:

  • Added support for both dict and Pydantic object event types in stream transformers
  • Fixed API streaming to properly await the send_async() coroutine
  • Restructured tool format from nested to flat structure for OpenRouter API compatibility
  • Added 4 runnable example scripts and QUICKSTART.md guide

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/openrouter/call_model/stream_transformers.py Added Pydantic object handling alongside dict events for API compatibility
src/openrouter/call_model/reusable_stream.py Updated type annotations to support both dict and Pydantic object events
src/openrouter/call_model/response_wrapper.py Fixed API call to await send_async() coroutine and updated stream type annotations
src/openrouter/call_model/call_model.py Restructured tool format from nested to flat structure for OpenRouter API
examples/call_model_with_tools.py Added comprehensive tool usage example with weather demonstration
examples/call_model_streaming.py Added streaming example showing real-time text generation
examples/call_model_quickstart.py Added minimal quickstart example for getting started
examples/call_model_multiple_patterns.py Added example demonstrating multiple consumption patterns
README.md Added quick start section with code example and links to guides
QUICKSTART.md Added comprehensive quick start guide with progressive examples

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

client = OpenRouter(api_key=os.getenv("OPENROUTER_API_KEY"))

# Make ONE API call
print("📞 Making API call...\n")

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This emoji (📞) should be removed to maintain a professional tone, as per the PR description which states "Remove emojis and hyperbolic language for professional tone".

Suggested change
print("📞 Making API call...\n")
print("Making API call...\n")

Copilot uses AI. Check for mistakes.
Comment on lines +105 to +109
for attr in dir(event):
if not attr.startswith("_") and attr != "type":
value = getattr(event, attr, None)
if value is not None:
event_dict[attr] = value

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using dir() to iterate over attributes is problematic as it includes methods and properties in addition to data attributes. This could lead to unexpected behavior or errors if methods are called. Consider using __dict__ or explicitly listing expected attributes, or check if the attribute is callable before adding it to the dict.

Suggested change
for attr in dir(event):
if not attr.startswith("_") and attr != "type":
value = getattr(event, attr, None)
if value is not None:
event_dict[attr] = value
# Use __dict__ to get only data attributes
for attr, value in getattr(event, "__dict__", {}).items():
if not attr.startswith("_") and attr != "type" and value is not None:
event_dict[attr] = value

Copilot uses AI. Check for mistakes.
Comment on lines +92 to 116
# Convert Pydantic objects to dicts for consistent output
if hasattr(event, "type"):
# It's a Pydantic object from the API
event_type = getattr(event, "type", None)
if not isinstance(event_type, str):
continue

# Convert to dict for output
if hasattr(event, "model_dump"):
event_dict = cast(object, event).model_dump() # type: ignore
else:
# Fallback: manually construct dict
event_dict = {"type": event_type}
for attr in dir(event):
if not attr.startswith("_") and attr != "type":
value = getattr(event, attr, None)
if value is not None:
event_dict[attr] = value
elif isinstance(event, dict):
event_dict = event
event_type = event.get("type")
if not isinstance(event_type, str):
continue
else:
continue

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Pydantic object to dict conversion logic is duplicated across multiple functions (extract_text_deltas, extract_tool_events, and build_message_from_stream). Consider extracting this into a helper function like _convert_event_to_dict(event) to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
Comment thread README.md
Comment on lines +14 to +20
from openrouter import OpenRouter
from openrouter.call_model import call_model

client = OpenRouter(api_key="your-key-here")
response = await call_model(client, {"model": "openai/gpt-4", "input": "Hello!"})
text = await response.get_text()
print(text)

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code snippet shows await being used outside of an async function context. This will cause a SyntaxError if users try to run it as-is. Consider wrapping the code in an async function and showing asyncio.run() to make it a complete, runnable example.

Suggested change
from openrouter import OpenRouter
from openrouter.call_model import call_model
client = OpenRouter(api_key="your-key-here")
response = await call_model(client, {"model": "openai/gpt-4", "input": "Hello!"})
text = await response.get_text()
print(text)
import asyncio
from openrouter import OpenRouter
from openrouter.call_model import call_model
async def main():
client = OpenRouter(api_key="your-key-here")
response = await call_model(client, {"model": "openai/gpt-4", "input": "Hello!"})
text = await response.get_text()
print(text)
asyncio.run(main())

Copilot uses AI. Check for mistakes.
Add user-friendly documentation following progressive disclosure and
multiple learning style principles:

New Documentation:
- QUICKSTART.md: 5-minute guide from zero to hero
- Updated README.md with prominent quick start section

New Examples (all runnable):
- call_model_quickstart.py: First call in 3 lines
- call_model_streaming.py: Real-time streaming demonstration
- call_model_with_tools.py: AI with custom function calling
- call_model_multiple_patterns.py: One call, many consumption patterns

Design Principles Applied:
- Progressive disclosure: Simple → Intermediate → Advanced
- Multiple learning styles: Visual, kinesthetic, reading/writing
- Hero's journey: Users feel empowered, not overwhelmed
- Evidence from PREP docs: Addresses different audience values

Each example:
- Has clear learning objectives
- Includes helpful comments
- Shows expected output
- Has error handling
- Can run standalone

Documentation serves:
- Beginners: Get started fast (< 5 min)
- Intermediate: See patterns (streaming, tools)
- Advanced: Understand flexibility (multiple patterns)
- All: Feel capable and excited to build

Covers code-review checklist:
- Real problem: Users need to get started quickly
- Simplicity: Clearest possible examples
- Reversibility: Examples are non-destructive
- Diplomacy: Encouraging, not condescending
- 3 AM rule: Clear enough to debug easily
- Update stream_transformers.py to handle both dict and Pydantic event objects
- Fix ResponseWrapper to await send_async() coroutine properly
- Update tool format to match OpenRouter's flat structure (not nested)
- Update type signatures in reusable_stream.py for flexible event handling
- Remove decorative emojis from QUICKSTART.md and README.md
- Replace hyperbolic phrases ('zero to hero', 'infinite', 'superpowers')
- Use precise technical language instead of metaphors
- Maintain professional tone in all example files
@subtleGradient
subtleGradient force-pushed the tom/call-model-12-docs-examples branch from 76ac6b2 to 4aacbfc Compare November 21, 2025 03:49
@subtleGradient
subtleGradient force-pushed the tom/call-model-11-client-integration branch from cf46285 to 58da8c4 Compare November 21, 2025 03:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants