Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 27 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,34 @@ Notable additions, fixes, or breaking changes to the Freeplay SDK.

### Added

- **Gemini API Provider Support**: Added support for Google's Gemini API as a new provider option (`provider="gemini"`), complementing the existing Vertex AI provider. This provides a simpler authentication method using API keys instead of GCP service accounts.

```python
# Vertex AI (existing - uses GCP authentication)
client.recordings.create(
RecordPayload(
project_id=project_id,
all_messages=[...],
call_info=CallInfo(provider="vertex", model="gemini-1.5-pro")
)
)

# Gemini API (new - uses simple API key authentication)
client.recordings.create(
RecordPayload(
project_id=project_id,
all_messages=[...],
call_info=CallInfo(provider="gemini", model="gemini-2.0-flash")
)
)
```

**Note**: Both providers remain fully supported. Vertex AI is recommended for GCP-integrated environments, while Gemini API provides a simpler setup for standalone applications.

- Interactive REPL for development and testing:
- `make repl` - Production mode (connects to app.freeplay.ai with SSL verification enabled)
- `make repl-local` - Local development mode (connects to localhost:8000 with SSL verification disabled)
- `make repl ARGS="--local"` - Alternative syntax for local mode
- Pre-loaded imports (Freeplay client, etc.)
- Environment variables automatically loaded from `.env` file
- Pre-initialized `client` variable ready to use
Expand Down Expand Up @@ -43,13 +68,13 @@ Notable additions, fixes, or breaking changes to the Freeplay SDK.
}
]

# Use in recordings
# Use in recordings - works with both Vertex and Gemini providers
client.recordings.create(
RecordPayload(
project_id=project_id,
all_messages=[...],
tool_schema=tool_schema,
call_info=CallInfo(provider="vertex", model="gemini-2.0-flash")
call_info=CallInfo(provider="vertex", model="gemini-1.5-pro") # or provider="gemini"
)
)
```
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ run-%:
# Start interactive REPL with Freeplay client initialized
# By default connects to production (app.freeplay.ai)
# Use 'make repl-local' for local development with SSL bypass
# Or pass arguments: make repl ARGS="--local"
.PHONY: repl
repl:
set -a; source .env 2>/dev/null || true; set +a; uv run python -i scripts/repl_setup.py
set -a; source .env 2>/dev/null || true; set +a; uv run python -i scripts/repl_setup.py $(ARGS)

# Start REPL in local mode (connects to localhost, disables SSL verification)
.PHONY: repl-local
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,37 @@ fp_client.recordings.create(

See the [SDK Setup guide](https://docs.freeplay.ai/freeplay-sdk/setup) for complete examples.

## Supported Providers

Freeplay supports multiple LLM providers for recording and observability:

- **OpenAI** (`provider="openai"`)
- **Anthropic** (`provider="anthropic"`)
- **Azure OpenAI** (`provider="azure"`)
- **AWS Bedrock** (`provider="bedrock"`)
- **Google Vertex AI** (`provider="vertex"`) - Google's cloud AI platform
- **Google Gemini API** (`provider="gemini"`) - Google's API with simple key auth ⭐ **NEW**

### Gemini API vs Vertex AI

Both providers support Google's Gemini models but differ in setup:

| Feature | Vertex AI | Gemini API |
|---------|-----------|------------|
| **Authentication** | GCP Service Account | API Key |
| **Setup Complexity** | High (GCP infrastructure) | Low (just API key) |
| **Use Case** | Enterprise, GCP-integrated | Standalone applications |

```python
# Vertex AI (enterprise, GCP)
CallInfo(provider="vertex", model="gemini-1.5-pro")

# Gemini API (simple setup)
CallInfo(provider="gemini", model="gemini-2.0-flash")
```

See [`examples/gemini_api_example.py`](https://github.com/freeplayai/freeplay-python/blob/main/examples/gemini_api_example.py) for detailed examples.

## Configuration

### Environment variables
Expand Down
133 changes: 133 additions & 0 deletions examples/gemini_api_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"""
Example: Using the Gemini API Provider

This example demonstrates how to use Google's Gemini API provider with Freeplay.
The Gemini API provider uses simple API key authentication, compared to Vertex AI
which requires GCP service account authentication.

Both providers support the same Gemini models but differ in:
- Authentication method (API key vs. GCP credentials)
- Endpoint (generativelanguage.googleapis.com vs. aiplatform.googleapis.com)
- Setup complexity (simple vs. GCP infrastructure)

Prerequisites:
- Set FREEPLAY_API_KEY in your environment
- Set FREEPLAY_PROJECT_ID in your environment
- Gemini API configured in Freeplay UI with API key
"""

import os
from freeplay import Freeplay, RecordPayload, CallInfo

# Initialize Freeplay client
client = Freeplay(
freeplay_api_key=os.environ["FREEPLAY_API_KEY"],
api_base="https://app.freeplay.ai/api",
)

project_id = os.environ["FREEPLAY_PROJECT_ID"]

# Example 1: Basic recording with Gemini API
print("Example 1: Basic Gemini API recording")
response = client.recordings.create(
RecordPayload(
project_id=project_id,
all_messages=[
{"role": "user", "content": "What is the capital of France?"},
{"role": "assistant", "content": "The capital of France is Paris."},
],
call_info=CallInfo(
provider="gemini", # Use Gemini API provider
model="gemini-2.0-flash",
),
)
)
print(f"✅ Recorded with Gemini API: {response.completion_id}")

# Example 2: Gemini API with tool schema
print("\nExample 2: Gemini API with tool schema")

tool_schema = [
{
"functionDeclarations": [
{
"name": "get_weather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name, e.g., 'San Francisco'",
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature units",
},
},
"required": ["location"],
},
}
]
}
]

response = client.recordings.create(
RecordPayload(
project_id=project_id,
all_messages=[
{"role": "user", "content": "What's the weather in Tokyo?"},
{"role": "assistant", "content": "Let me check the weather for you."},
],
tool_schema=tool_schema,
call_info=CallInfo(
provider="gemini", # Use Gemini API provider
model="gemini-2.5-pro",
),
)
)
print(f"✅ Recorded with tool schema: {response.completion_id}")

# Example 3: Comparison - Vertex AI vs Gemini API
print("\nExample 3: Provider comparison")

# Vertex AI (GCP authentication)
vertex_response = client.recordings.create(
RecordPayload(
project_id=project_id,
all_messages=[
{"role": "user", "content": "Hello from Vertex AI"},
{"role": "assistant", "content": "Hello! I'm using Vertex AI."},
],
call_info=CallInfo(
provider="vertex", # Vertex AI provider
model="gemini-1.5-pro",
),
)
)
print(f"✅ Vertex AI: {vertex_response.completion_id}")

# Gemini API (simple API key)
gemini_response = client.recordings.create(
RecordPayload(
project_id=project_id,
all_messages=[
{"role": "user", "content": "Hello from Gemini API"},
{"role": "assistant", "content": "Hello! I'm using Gemini API."},
],
call_info=CallInfo(
provider="gemini", # Gemini API provider
model="gemini-2.0-flash",
),
)
)
print(f"✅ Gemini API: {gemini_response.completion_id}")

print("\n" + "=" * 50)
print("Summary:")
print("- Both providers support Gemini models")
print("- Vertex AI: GCP integration, enterprise features")
print("- Gemini API: Simple API key, quick setup")
print("- Choose based on your infrastructure needs")
print("=" * 50)
2 changes: 1 addition & 1 deletion src/freeplay/resources/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ def __flavor_to_provider(flavor: str) -> str:
"azure_openai_chat": "azure",
"anthropic_chat": "anthropic",
"openai_chat": "openai",
"gemini_chat": "vertex",
"gemini_chat": "vertex", # Default to vertex for backward compatibility
}
provider = flavor_provider.get(flavor)
if not provider:
Expand Down
15 changes: 15 additions & 0 deletions src/freeplay/resources/recordings.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ class UsageTokens:

@dataclass
class CallInfo:
"""
Information about an LLM call.

Attributes:
provider: LLM provider name. Supported: "openai", "anthropic", "azure",
"bedrock", "vertex", "gemini". Use "vertex" for Vertex AI (GCP),
"gemini" for Gemini API (simple API key).
model: Model identifier (e.g., "gpt-4", "gemini-2.0-flash")
start_time: Unix timestamp when the call started
end_time: Unix timestamp when the call ended
model_parameters: Model configuration (temperature, max_tokens, etc.)
provider_info: Additional provider-specific metadata
usage: Token usage information
api_style: API style (chat, completion, etc.)
"""
provider: Optional[str] = None
model: Optional[str] = None
start_time: Optional[float] = None
Expand Down
24 changes: 23 additions & 1 deletion tests/test_freeplay.py
Original file line number Diff line number Diff line change
Expand Up @@ -1422,7 +1422,7 @@ def test_prompt_format_with_tool_schema_gemini(self) -> None:
environment="environment",
model_parameters=LLMParameters({}),
provider_info=None,
provider="vertex",
provider="gemini",
model="gemini-pro",
flavor_name="gemini_chat",
)
Expand Down Expand Up @@ -1458,6 +1458,28 @@ def test_prompt_format_with_tool_schema_gemini(self) -> None:
except ImportError:
self.skipTest("Vertex AI SDK not installed")

def test_flavor_to_provider_mapping(self) -> None:
"""Test that flavor names correctly map to provider names"""
from freeplay.resources.adapters import MissingFlavorError

# Test the private method through its name-mangled form
# Type checker doesn't like accessing private methods, so we use type: ignore
flavor_to_provider = ( # type: ignore[attr-defined]
FilesystemTemplateResolver._FilesystemTemplateResolver__flavor_to_provider # type: ignore[attr-defined]
)

# Test all supported flavor mappings
self.assertEqual(flavor_to_provider("openai_chat"), "openai")
self.assertEqual(flavor_to_provider("azure_openai_chat"), "azure")
self.assertEqual(flavor_to_provider("anthropic_chat"), "anthropic")
self.assertEqual(
flavor_to_provider("gemini_chat"), "gemini"
) # Key test: gemini_chat -> gemini

# Test that unknown flavors raise an error
with self.assertRaises(MissingFlavorError):
flavor_to_provider("unknown_flavor")

def test_prompt_format_with_output_schema_openai(self) -> None:
messages: List[TemplateMessage] = [
TemplateChatMessage(role="system", content="System message"),
Expand Down
Loading