Skip to content

Commit dc879d5

Browse files
feat: Enhanced SDK support with Organizations, Users, and unified CodegenSDK
- Added comprehensive exception handling with custom error types - Implemented Organizations API client with pagination support - Implemented Users API client with search and filtering capabilities - Enhanced Agent class with better error handling and helper methods - Created unified CodegenSDK class as main entry point - Added comprehensive examples and documentation - Maintained backward compatibility with existing Agent interface This addresses CG-18852 by providing a more complete set of operations with intuitive objects that make sense, building on the existing OpenAPI spec from codegen-api-client.
1 parent d23047f commit dc879d5

File tree

9 files changed

+810
-30
lines changed

9 files changed

+810
-30
lines changed

examples/sdk_usage.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
"""
2+
Example usage of the enhanced Codegen SDK.
3+
4+
This example demonstrates how to use the new SDK components:
5+
- CodegenSDK: Unified entry point for all API functionality
6+
- Organizations: Managing organizations
7+
- Users: Managing users within organizations
8+
- Enhanced Agent: Improved agent functionality with better error handling
9+
"""
10+
11+
import os
12+
from codegen.agents import Agent, CodegenSDK, Organizations, Users
13+
14+
# Example 1: Using the unified SDK
15+
def example_unified_sdk():
16+
"""Example using the unified CodegenSDK."""
17+
print("=== Unified SDK Example ===")
18+
19+
# Initialize the SDK with your API token
20+
sdk = CodegenSDK(token=os.getenv("CODEGEN_API_TOKEN"))
21+
22+
# Use agents
23+
print("Running an agent task...")
24+
task = sdk.agents.run("Analyze the codebase and suggest improvements")
25+
print(f"Task created: {task.id}")
26+
print(f"Status: {task.status}")
27+
print(f"Web URL: {task.web_url}")
28+
29+
# List organizations
30+
print("\nListing organizations...")
31+
orgs = sdk.organizations.list()
32+
for org in orgs:
33+
print(f"- {org.name} (ID: {org.id})")
34+
35+
# Get users in the first organization
36+
if orgs:
37+
print(f"\nListing users in organization '{orgs[0].name}'...")
38+
users = sdk.users(org_id=orgs[0].id).list()
39+
for user in users:
40+
print(f"- {user.github_username} ({user.email})")
41+
42+
43+
# Example 2: Using individual SDK components
44+
def example_individual_components():
45+
"""Example using individual SDK components."""
46+
print("\n=== Individual Components Example ===")
47+
48+
token = os.getenv("CODEGEN_API_TOKEN")
49+
50+
# Enhanced Agent with better error handling
51+
print("Using enhanced Agent...")
52+
agent = Agent(token=token)
53+
54+
try:
55+
task = agent.run("Create a simple Python function")
56+
print(f"Agent task: {task.id}")
57+
58+
# Check task status with new helper methods
59+
if task.is_running():
60+
print("Task is currently running...")
61+
elif task.is_completed():
62+
print("Task completed!")
63+
if task.is_successful():
64+
print("Task was successful!")
65+
elif task.is_failed():
66+
print("Task failed.")
67+
68+
# Get task by ID
69+
retrieved_task = agent.get_task(task.id)
70+
print(f"Retrieved task: {retrieved_task.id}")
71+
72+
except Exception as e:
73+
print(f"Error: {e}")
74+
75+
# Organizations management
76+
print("\nUsing Organizations...")
77+
orgs_client = Organizations(token=token)
78+
79+
try:
80+
# List with pagination
81+
page_result = orgs_client.get_page(page=1, page_size=10)
82+
print(f"Organizations page: {page_result['page']}/{page_result['pages']}")
83+
print(f"Total organizations: {page_result['total']}")
84+
85+
# Iterate through all organizations
86+
print("All organizations:")
87+
for org in orgs_client.list_all():
88+
print(f"- {org.name} (ID: {org.id})")
89+
print(f" Settings: {org.settings}")
90+
91+
except Exception as e:
92+
print(f"Error: {e}")
93+
94+
# Users management
95+
print("\nUsing Users...")
96+
if orgs:
97+
users_client = Users(token=token, org_id=orgs[0].id)
98+
99+
try:
100+
# List users with pagination
101+
page_result = users_client.get_page(page=1, page_size=5)
102+
print(f"Users page: {page_result['page']}/{page_result['pages']}")
103+
print(f"Total users: {page_result['total']}")
104+
105+
# Find specific users
106+
user = users_client.find_by_github_username("example-user")
107+
if user:
108+
print(f"Found user: {user.github_username}")
109+
110+
# Get user by ID
111+
if page_result['items']:
112+
first_user = page_result['items'][0]
113+
retrieved_user = users_client.get(first_user.id)
114+
print(f"Retrieved user: {retrieved_user.github_username}")
115+
116+
except Exception as e:
117+
print(f"Error: {e}")
118+
119+
120+
# Example 3: Error handling
121+
def example_error_handling():
122+
"""Example demonstrating error handling."""
123+
print("\n=== Error Handling Example ===")
124+
125+
from codegen.exceptions import AuthenticationError, NotFoundError, CodegenError
126+
127+
# Invalid token example
128+
try:
129+
sdk = CodegenSDK(token="invalid-token")
130+
orgs = sdk.organizations.list()
131+
except AuthenticationError as e:
132+
print(f"Authentication failed: {e.message}")
133+
print(f"Status code: {e.status_code}")
134+
except CodegenError as e:
135+
print(f"API error: {e.message}")
136+
137+
# Not found example
138+
try:
139+
sdk = CodegenSDK(token=os.getenv("CODEGEN_API_TOKEN"))
140+
user = sdk.users().get(user_id=99999) # Non-existent user
141+
except NotFoundError as e:
142+
print(f"User not found: {e.message}")
143+
except CodegenError as e:
144+
print(f"API error: {e.message}")
145+
146+
147+
if __name__ == "__main__":
148+
# Make sure to set your API token
149+
if not os.getenv("CODEGEN_API_TOKEN"):
150+
print("Please set the CODEGEN_API_TOKEN environment variable")
151+
exit(1)
152+
153+
example_unified_sdk()
154+
example_individual_components()
155+
example_error_handling()

src/codegen/agents/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,9 @@
22

33
from codegen.agents.agent import Agent
44

5-
__all__ = ["Agent"]
5+
# Import SDK components for backward compatibility and convenience
6+
from codegen.sdk import CodegenSDK
7+
from codegen.organizations import Organizations
8+
from codegen.users import Users
9+
10+
__all__ = ["Agent", "CodegenSDK", "Organizations", "Users"]

src/codegen/agents/agent.py

Lines changed: 98 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import os
2-
from typing import Any
2+
from typing import Any, Dict, Optional, Union
33

44
from codegen_api_client.api.agents_api import AgentsApi
55
from codegen_api_client.api_client import ApiClient
66
from codegen_api_client.configuration import Configuration
77
from codegen_api_client.models.agent_run_response import AgentRunResponse
88
from codegen_api_client.models.create_agent_run_input import CreateAgentRunInput
9+
from codegen_api_client.rest import ApiException
910

1011
from codegen.agents.constants import CODEGEN_BASE_API_URL
12+
from codegen.exceptions import handle_api_error
1113

1214

1315
class AgentTask:
@@ -23,34 +25,69 @@ def __init__(self, task_data: AgentRunResponse, api_client: ApiClient, org_id: i
2325
self._agents_api = AgentsApi(api_client)
2426

2527
def refresh(self) -> None:
26-
"""Refresh the job status from the API."""
28+
"""Refresh the job status from the API.
29+
30+
Raises:
31+
CodegenError: If the API request fails
32+
"""
2733
if self.id is None:
2834
return
2935

30-
job_data = self._agents_api.get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get(
31-
agent_run_id=int(self.id), org_id=int(self.org_id), authorization=f"Bearer {self._api_client.configuration.access_token}"
32-
)
33-
34-
# Convert API response to dict for attribute access
35-
job_dict = {}
36-
if hasattr(job_data, "__dict__"):
37-
job_dict = job_data.__dict__
38-
elif isinstance(job_data, dict):
39-
job_dict = job_data
40-
41-
self.status = job_dict.get("status")
42-
self.result = job_dict.get("result")
36+
try:
37+
job_data = self._agents_api.get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get(
38+
agent_run_id=int(self.id), org_id=int(self.org_id), authorization=f"Bearer {self._api_client.configuration.access_token}"
39+
)
40+
41+
# Convert API response to dict for attribute access
42+
job_dict = {}
43+
if hasattr(job_data, "__dict__"):
44+
job_dict = job_data.__dict__
45+
elif isinstance(job_data, dict):
46+
job_dict = job_data
47+
48+
self.status = job_dict.get("status")
49+
self.result = job_dict.get("result")
50+
except ApiException as e:
51+
error = handle_api_error(e.status, str(e), getattr(e, 'body', None))
52+
raise error from e
53+
54+
def is_completed(self) -> bool:
55+
"""Check if the agent task is completed."""
56+
return self.status in ['completed', 'failed', 'cancelled']
57+
58+
def is_running(self) -> bool:
59+
"""Check if the agent task is currently running."""
60+
return self.status in ['running', 'pending']
61+
62+
def is_successful(self) -> bool:
63+
"""Check if the agent task completed successfully."""
64+
return self.status == 'completed'
65+
66+
def is_failed(self) -> bool:
67+
"""Check if the agent task failed."""
68+
return self.status == 'failed'
69+
70+
def to_dict(self) -> Dict[str, Any]:
71+
"""Convert agent task to dictionary."""
72+
return {
73+
'id': self.id,
74+
'org_id': self.org_id,
75+
'status': self.status,
76+
'result': self.result,
77+
'web_url': self.web_url
78+
}
4379

4480

4581
class Agent:
4682
"""API client for interacting with Codegen AI agents."""
4783

48-
def __init__(self, token: str, org_id: int | None = None, base_url: str | None = CODEGEN_BASE_API_URL):
84+
def __init__(self, token: str, org_id: Optional[Union[int, str]] = None, base_url: str = CODEGEN_BASE_API_URL):
4985
"""Initialize a new Agent client.
5086
5187
Args:
5288
token: API authentication token
5389
org_id: Optional organization ID. If not provided, default org will be used.
90+
base_url: Base URL for the API (defaults to production)
5491
"""
5592
self.token = token
5693
self.org_id = org_id or int(os.environ.get("CODEGEN_ORG_ID", "1")) # Default to org ID 1 if not specified
@@ -61,7 +98,7 @@ def __init__(self, token: str, org_id: int | None = None, base_url: str | None =
6198
self.agents_api = AgentsApi(self.api_client)
6299

63100
# Current job
64-
self.current_job: AgentTask | None = None
101+
self.current_job: Optional[AgentTask] = None
65102

66103
def run(self, prompt: str) -> AgentTask:
67104
"""Run an agent with the given prompt.
@@ -70,26 +107,58 @@ def run(self, prompt: str) -> AgentTask:
70107
prompt: The instruction for the agent to execute
71108
72109
Returns:
73-
Job: A job object representing the agent run
110+
AgentTask: A task object representing the agent run
111+
112+
Raises:
113+
CodegenError: If the API request fails
74114
"""
75-
run_input = CreateAgentRunInput(prompt=prompt)
76-
agent_run_response = self.agents_api.create_agent_run_v1_organizations_org_id_agent_run_post(
77-
org_id=int(self.org_id), create_agent_run_input=run_input, authorization=f"Bearer {self.token}", _headers={"Content-Type": "application/json"}
78-
)
79-
# Convert API response to dict for Job initialization
80-
81-
job = AgentTask(agent_run_response, self.api_client, self.org_id)
82-
self.current_job = job
83-
return job
84-
85-
def get_status(self) -> dict[str, Any] | None:
115+
try:
116+
run_input = CreateAgentRunInput(prompt=prompt)
117+
agent_run_response = self.agents_api.create_agent_run_v1_organizations_org_id_agent_run_post(
118+
org_id=int(self.org_id), create_agent_run_input=run_input, authorization=f"Bearer {self.token}", _headers={"Content-Type": "application/json"}
119+
)
120+
121+
job = AgentTask(agent_run_response, self.api_client, self.org_id)
122+
self.current_job = job
123+
return job
124+
except ApiException as e:
125+
error = handle_api_error(e.status, str(e), getattr(e, 'body', None))
126+
raise error from e
127+
128+
def get_status(self) -> Optional[Dict[str, Any]]:
86129
"""Get the status of the current job.
87130
88131
Returns:
89132
dict: A dictionary containing job status information,
90133
or None if no job has been run.
134+
135+
Raises:
136+
CodegenError: If the API request fails
91137
"""
92138
if self.current_job:
93139
self.current_job.refresh()
94140
return {"id": self.current_job.id, "status": self.current_job.status, "result": self.current_job.result, "web_url": self.current_job.web_url}
95141
return None
142+
143+
def get_task(self, task_id: Union[int, str]) -> AgentTask:
144+
"""Get a specific agent task by ID.
145+
146+
Args:
147+
task_id: Agent task ID to retrieve
148+
149+
Returns:
150+
AgentTask: The requested agent task
151+
152+
Raises:
153+
CodegenError: If the API request fails or task is not found
154+
"""
155+
try:
156+
response = self.agents_api.get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get(
157+
org_id=int(self.org_id),
158+
agent_run_id=int(task_id),
159+
authorization=f"Bearer {self.token}"
160+
)
161+
return AgentTask(response, self.api_client, self.org_id)
162+
except ApiException as e:
163+
error = handle_api_error(e.status, str(e), getattr(e, 'body', None))
164+
raise error from e

0 commit comments

Comments
 (0)