diff --git a/CHANGELOG.md b/CHANGELOG.md index 856c364..f9d85b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,12 @@ # Changelog All notable changes to this project's latest version. -## [0.1.30] - 2025-11-13 - -### Features - -- Enhance error handling in RunAgentClient and WebSocket clients +## [0.1.31] - 2025-11-13 ### Miscellaneous Tasks -- Bump version to v0.1.30 +- Add repository information to package.json +- Simplify create-release workflow by removing pull request trigger +- Bump version to v0.1.31 diff --git a/leads_20251102_115848.csv b/leads_20251102_115848.csv deleted file mode 100644 index 090aecb..0000000 --- a/leads_20251102_115848.csv +++ /dev/null @@ -1,4 +0,0 @@ -Website URL,Username,Bio,Post Type,Timestamp,Upvotes,Links -https://www.quora.com/Where-can-I-hire-someone-to-create-chatbot-customer-service,Quora User,Lead from Quora - Manual review needed,discussion,Recent,0,https://www.quora.com/Where-can-I-hire-someone-to-create-chatbot-customer-service -https://www.quora.com/What-is-the-most-realistic-AI-based-chatbot-website,Quora User,Lead from Quora - Manual review needed,discussion,Recent,0,https://www.quora.com/What-is-the-most-realistic-AI-based-chatbot-website -https://www.quora.com/Which-is-the-best-chatbot-provider-for-customer-service,Quora User,Lead from Quora - Manual review needed,discussion,Recent,0,https://www.quora.com/Which-is-the-best-chatbot-provider-for-customer-service diff --git a/leads_20251102_120327.csv b/leads_20251102_120327.csv deleted file mode 100644 index fe20b58..0000000 --- a/leads_20251102_120327.csv +++ /dev/null @@ -1,4 +0,0 @@ -Website URL,Username,Bio,Post Type,Timestamp,Upvotes,Links -https://www.quora.com/What-is-the-most-realistic-AI-based-chatbot-website,Quora User,Lead from Quora - Manual review needed,discussion,Recent,0,https://www.quora.com/What-is-the-most-realistic-AI-based-chatbot-website -https://www.quora.com/Where-can-I-hire-someone-to-create-chatbot-customer-service,Quora User,Lead from Quora - Manual review needed,discussion,Recent,0,https://www.quora.com/Where-can-I-hire-someone-to-create-chatbot-customer-service -https://www.quora.com/Where-can-I-find-an-AI-charbot-service,Quora User,Lead from Quora - Manual review needed,discussion,Recent,0,https://www.quora.com/Where-can-I-find-an-AI-charbot-service diff --git a/pyproject.toml b/pyproject.toml index 54de3b6..4780878 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "runagent" -version = "0.1.31" +version = "0.1.32" description = "A command-line tool and SDK for deploying, managing, and interacting with AI agents" readme = "README.md" requires-python = ">=3.9" @@ -103,7 +103,7 @@ line_length = 88 skip = ["docs"] [tool.mypy] -python_version = "0.1.31" +python_version = "0.1.32" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true @@ -159,7 +159,7 @@ fail_under = 80 [tool.ruff] line-length = 88 -target-version = "0.1.31" +target-version = "0.1.32" select = [ "E", # pycodestyle errors "W", # pycodestyle warnings diff --git a/runagent-go/runagent/pkg/client/client.go b/runagent-go/runagent/pkg/client/client.go index 4a373fe..7c20518 100644 --- a/runagent-go/runagent/pkg/client/client.go +++ b/runagent-go/runagent/pkg/client/client.go @@ -363,8 +363,53 @@ func (c *Client) GetAgentArchitecture(ctx context.Context) (*types.AgentArchitec return nil, types.NewServerError(fmt.Sprintf("Server returned status %d", resp.StatusCode)) } + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read architecture response: %w", err) + } + + var envelope struct { + Success bool `json:"success"` + Data struct { + AgentID string `json:"agent_id"` + Entrypoints []types.EntryPoint `json:"entrypoints"` + } `json:"data"` + Message string `json:"message"` + Error interface{} `json:"error"` + } + + if err := json.Unmarshal(body, &envelope); err == nil && (envelope.Success || envelope.Message != "" || envelope.Error != nil) { + if envelope.Success { + architecture := &types.AgentArchitecture{ + AgentID: envelope.Data.AgentID, + Entrypoints: envelope.Data.Entrypoints, + } + return architecture, nil + } + + var message string + switch errInfo := envelope.Error.(type) { + case map[string]interface{}: + if m, ok := errInfo["message"].(string); ok { + message = m + } + case string: + message = errInfo + } + + if message == "" { + message = envelope.Message + } + if message == "" { + message = "failed to retrieve agent architecture" + } + + return nil, types.NewServerError(message) + } + + // Fallback to legacy format without envelope var architecture types.AgentArchitecture - if err := json.NewDecoder(resp.Body).Decode(&architecture); err != nil { + if err := json.Unmarshal(body, &architecture); err != nil { return nil, fmt.Errorf("failed to decode architecture: %w", err) } diff --git a/runagent-go/runagent/pkg/types/types.go b/runagent-go/runagent/pkg/types/types.go index 1c3e162..14431ef 100644 --- a/runagent-go/runagent/pkg/types/types.go +++ b/runagent-go/runagent/pkg/types/types.go @@ -43,13 +43,17 @@ func NewConfigError(message string) *RunAgentError { // EntryPoint represents an agent entrypoint type EntryPoint struct { - File string `json:"file"` - Module string `json:"module"` - Tag string `json:"tag"` + File string `json:"file,omitempty"` + Module string `json:"module,omitempty"` + Tag string `json:"tag"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Extractor map[string]interface{} `json:"extractor,omitempty"` } // AgentArchitecture represents agent configuration type AgentArchitecture struct { + AgentID string `json:"agent_id,omitempty"` Entrypoints []EntryPoint `json:"entrypoints"` } diff --git a/runagent-go/runagent/version.go b/runagent-go/runagent/version.go index f9729eb..b38a704 100644 --- a/runagent-go/runagent/version.go +++ b/runagent-go/runagent/version.go @@ -1,4 +1,4 @@ package runagent // Version represents the current version of the RunAgent Go SDK -const Version = "0.1.31" +const Version = "0.1.32" diff --git a/runagent-rust/runagent/Cargo.toml b/runagent-rust/runagent/Cargo.toml index 6ab4aae..1a75289 100644 --- a/runagent-rust/runagent/Cargo.toml +++ b/runagent-rust/runagent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runagent" -version = "0.1.31" +version = "0.1.32" edition = "2021" description = "RunAgent SDK for Rust - Client SDK for interacting with deployed AI agents" license = "MIT" diff --git a/runagent-rust/runagent/src/client/rest_client.rs b/runagent-rust/runagent/src/client/rest_client.rs index aa21409..fc2eaba 100644 --- a/runagent-rust/runagent/src/client/rest_client.rs +++ b/runagent-rust/runagent/src/client/rest_client.rs @@ -221,6 +221,39 @@ impl RestClient { pub async fn get_agent_architecture(&self, agent_id: &str) -> RunAgentResult { let path = format!("agents/{}/architecture", agent_id); self.get(&path).await + .and_then(|response| { + if let Some(success) = response.get("success").and_then(|v| v.as_bool()) { + if success { + if let Some(data) = response.get("data") { + return Ok(data.clone()); + } + return Err(RunAgentError::server( + "Architecture response missing data".to_string(), + )); + } + + let message = response + .get("error") + .and_then(|err| { + if err.is_object() { + err.get("message").and_then(|m| m.as_str()).map(|s| s.to_string()) + } else { + err.as_str().map(|s| s.to_string()) + } + }) + .or_else(|| { + response + .get("message") + .and_then(|m| m.as_str()) + .map(|s| s.to_string()) + }) + .unwrap_or_else(|| "Failed to retrieve agent architecture".to_string()); + + return Err(RunAgentError::server(message)); + } + + Ok(response) + }) .map_err(|e| { if e.category() == "validation" && e.to_string().contains("Not found") { RunAgentError::validation(format!( diff --git a/runagent-ts/package-lock.json b/runagent-ts/package-lock.json index f8fb202..aae5f2d 100644 --- a/runagent-ts/package-lock.json +++ b/runagent-ts/package-lock.json @@ -1,12 +1,12 @@ { "name": "runagent", - "version": "0.1.31", + "version": "0.1.32", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "runagent", - "version": "0.1.31", + "version": "0.1.32", "dependencies": { "better-sqlite3": "^12.2.0" }, diff --git a/runagent-ts/package.json b/runagent-ts/package.json index d49116f..48e4144 100644 --- a/runagent-ts/package.json +++ b/runagent-ts/package.json @@ -1,6 +1,6 @@ { "name": "runagent", - "version": "0.1.31", + "version": "0.1.32", "type": "module", "files": [ "dist" diff --git a/runagent-ts/src/client/index.ts b/runagent-ts/src/client/index.ts index 16fdb17..8bf45fc 100644 --- a/runagent-ts/src/client/index.ts +++ b/runagent-ts/src/client/index.ts @@ -277,7 +277,16 @@ export class RunAgentClient { } } catch (error) { console.error('❌ Failed to initialize agent:', error); - throw error; + if (error instanceof RunAgentExecutionError) { + throw error; + } + const message = + error instanceof Error ? error.message : 'Unknown initialization error'; + throw new RunAgentExecutionError( + 'INITIALIZATION_ERROR', + this.sanitizeMessage(message) ?? 'Failed to initialize agent', + 'Verify the agent configuration and connection settings.' + ); } } diff --git a/runagent-ts/src/rest/index.ts b/runagent-ts/src/rest/index.ts index a048cd9..f9ce927 100644 --- a/runagent-ts/src/rest/index.ts +++ b/runagent-ts/src/rest/index.ts @@ -3,6 +3,7 @@ import { AuthenticationError, ClientError, ConnectionError, + RunAgentExecutionError, ServerError, ValidationError, } from '../errors/index.js'; @@ -27,6 +28,7 @@ export class RestClient { private baseUrl: string; private apiKey?: string; private defaultTimeoutSeconds: number; + private isLocal: boolean; constructor(options: RestClientConfig = {}) { const { @@ -36,10 +38,11 @@ export class RestClient { isLocal = true, timeoutSeconds = 300, } = options; - + this.baseUrl = baseUrl.replace(/\/$/, '') + apiPrefix; this.apiKey = apiKey; this.defaultTimeoutSeconds = timeoutSeconds; + this.isLocal = isLocal; this.http = new HttpHandler(this.apiKey, this.baseUrl, isLocal); } @@ -54,20 +57,20 @@ export class RestClient { timeoutSeconds = this.defaultTimeoutSeconds, } = options; - const requestData = { + const requestData = { entrypoint_tag: entrypointTag, - input_args: inputArgs, - input_kwargs: inputKwargs, + input_args: inputArgs, + input_kwargs: inputKwargs, timeout_seconds: timeoutSeconds, async_execution: false, - } as JsonValue; - + } as JsonValue; + const timeoutMs = timeoutSeconds * 1000 + 10_000; // Add buffer similar to Python SDK - try { - const response = await this.http.post( + try { + const response = await this.http.post( `/agents/${agentId}/run`, - requestData, + requestData, { timeout: timeoutMs } ); @@ -153,16 +156,174 @@ export class RestClient { async getAgentArchitecture(agentId: string): Promise { try { const response = await this.http.get(`/agents/${agentId}/architecture`); - return (await response.json()) as AgentArchitecture; + const parsed = (await response.json()) as + | ApiResponse + | AgentArchitecture; + + if (parsed && typeof parsed === 'object' && 'success' in parsed) { + const envelope = parsed as ApiResponse; + + if (envelope.success === false) { + const { code, message, suggestion, details } = + this.extractArchitectureError(envelope); + throw new RunAgentExecutionError(code, message, suggestion, details); + } + + if (!envelope.data) { + throw new RunAgentExecutionError( + 'ARCHITECTURE_MISSING', + 'Response did not include agent architecture data.' + ); + } + + return this.normalizeArchitecture(envelope.data); + } + + return this.normalizeArchitecture(parsed as AgentArchitecture); } catch (error) { - throw new Error( - `Failed to get architecture: ${ - error instanceof Error ? error.message : 'Unknown error' - }` + if (error instanceof RunAgentExecutionError) { + throw error; + } + + if (error instanceof ClientError && error.statusCode === 404) { + if (this.isLocal) { + throw new RunAgentExecutionError( + 'AGENT_NOT_FOUND_LOCAL', + `Agent ${agentId} is not registered in your local RunAgent database.`, + "Start the agent locally with `runagent serve` or provide the agent's host and port." + ); + } + throw new RunAgentExecutionError( + 'AGENT_NOT_FOUND_REMOTE', + `Agent ${agentId} was not found in RunAgent Cloud.`, + 'Confirm the agent ID in the dashboard or set `local: true` with host/port to call a local agent.' + ); + } + + if (error instanceof AuthenticationError) { + throw new RunAgentExecutionError( + 'AUTHENTICATION_ERROR', + error.message, + 'Verify that RUNAGENT_API_KEY is set and valid.' + ); + } + + if (error instanceof ValidationError) { + throw new RunAgentExecutionError( + 'VALIDATION_ERROR', + error.message, + 'Check the request configuration or agent settings.' + ); + } + + if (error instanceof ConnectionError) { + throw new RunAgentExecutionError( + 'CONNECTION_ERROR', + error.message, + 'Ensure the RunAgent service URL is reachable.' + ); + } + + if (error instanceof ServerError) { + throw new RunAgentExecutionError( + 'SERVER_ERROR', + error.message, + 'Try again later or contact support if the issue persists.' + ); + } + + if (error instanceof ClientError) { + throw new RunAgentExecutionError( + 'CLIENT_ERROR', + error.message, + 'Review the request and agent configuration.' + ); + } + + const message = + error instanceof Error + ? error.message + : 'Failed to retrieve agent architecture.'; + + throw new RunAgentExecutionError( + this.isLocal + ? 'ARCHITECTURE_FETCH_LOCAL_ERROR' + : 'ARCHITECTURE_FETCH_REMOTE_ERROR', + message, + this.isLocal + ? 'Ensure the agent is running locally or provide host/port explicitly.' + : 'Verify the agent exists and that your API key/base URL are correct.' ); } } + private normalizeArchitecture( + data?: AgentArchitecture + ): AgentArchitecture { + if (!data) { + return { entrypoints: [] }; + } + + const agentId = + data.agentId ?? + (data as AgentArchitecture & { agent_id?: string }).agent_id; + + const entrypoints = Array.isArray(data.entrypoints) + ? data.entrypoints.map((entry) => ({ ...entry })) + : []; + + return { + ...data, + agentId, + agent_id: + (data as AgentArchitecture & { agent_id?: string }).agent_id ?? agentId, + entrypoints, + }; + } + + private extractArchitectureError( + envelope: ApiResponse + ): { + code: string; + message: string; + suggestion?: string | null; + details?: unknown; + } { + const defaultCode = this.isLocal + ? 'AGENT_NOT_FOUND_LOCAL' + : 'AGENT_NOT_FOUND_REMOTE'; + + if (!envelope.error) { + return { + code: defaultCode, + message: envelope.message ?? 'Failed to retrieve agent architecture.', + suggestion: this.isLocal + ? 'Ensure the agent is running locally or provide host/port explicitly.' + : 'Verify the agent exists and that your API key/base URL are correct.', + }; + } + + if (typeof envelope.error === 'string') { + return { + code: defaultCode, + message: envelope.error, + suggestion: this.isLocal + ? 'Ensure the agent is running locally or provide host/port explicitly.' + : 'Verify the agent exists and that your API key/base URL are correct.', + }; + } + + return { + code: envelope.error.code ?? defaultCode, + message: + envelope.error.message ?? + envelope.message ?? + 'Failed to retrieve agent architecture.', + suggestion: envelope.error.suggestion ?? undefined, + details: envelope.error.details, + }; + } + close(): void { // HTTP handler cleanup if needed } diff --git a/runagent-ts/src/types/index.ts b/runagent-ts/src/types/index.ts index 0334548..a139c3e 100644 --- a/runagent-ts/src/types/index.ts +++ b/runagent-ts/src/types/index.ts @@ -40,13 +40,20 @@ export interface WebSocketMessage { error?: string; } +export interface AgentEntrypoint { + tag: string; + name?: string; + description?: string; + file?: string; + module?: string; + extractor?: Record; +} + export interface AgentArchitecture { - entrypoints: Array<{ - tag: string; - name?: string; - description?: string; - }>; - } + agent_id?: string; + agentId?: string; + entrypoints: AgentEntrypoint[]; +} export interface ExecutionRequest { entrypoint_tag: string; diff --git a/runagent/__init__.py b/runagent/__init__.py index b7443c1..8dcaab7 100644 --- a/runagent/__init__.py +++ b/runagent/__init__.py @@ -5,7 +5,7 @@ built with frameworks like LangGraph, LangChain, and LlamaIndex. """ -__version__ = "0.1.31" +__version__ = "0.1.32" from .client import RunAgentClient diff --git a/runagent/__version__.py b/runagent/__version__.py index b84359f..57cc51c 100644 --- a/runagent/__version__.py +++ b/runagent/__version__.py @@ -1 +1 @@ -__version__ = "0.1.31" +__version__ = "0.1.32" diff --git a/runagent/sdk/rest_client.py b/runagent/sdk/rest_client.py index 19e25b9..849d020 100644 --- a/runagent/sdk/rest_client.py +++ b/runagent/sdk/rest_client.py @@ -1598,7 +1598,21 @@ def get_agent_architecture(self, agent_id: str) -> Dict: """Get the architecture information for a specific agent""" try: response = self.http.get(f"/agents/{agent_id}/architecture") - return response.json() + payload = response.json() + + if isinstance(payload, dict) and "success" in payload: + if payload.get("success"): + return payload.get("data") or {} + + error_info = payload.get("error") + if isinstance(error_info, dict): + message = error_info.get("message") or payload.get("message") or "Failed to get architecture" + else: + message = error_info or payload.get("message") or "Failed to get architecture" + + raise ValueError(message) + + return payload except Exception as e: if os.getenv('DISABLE_TRY_CATCH'): raise diff --git a/runagent/sdk/server/local_server.py b/runagent/sdk/server/local_server.py index 5297812..b29d5ff 100644 --- a/runagent/sdk/server/local_server.py +++ b/runagent/sdk/server/local_server.py @@ -587,8 +587,17 @@ async def health_check(): async def get_agent_architecture(): """Get agent architecture endpoint""" return { - "agent_id": self.agent_id, - "entrypoints": [ep.dict() for ep in self.agent_config.agent_architecture.entrypoints] + "success": True, + "data": { + "agent_id": self.agent_id, + "entrypoints": [ + ep.dict() for ep in self.agent_config.agent_architecture.entrypoints + ], + }, + "message": "Agent architecture retrieved successfully", + "error": None, + "timestamp": datetime.now().isoformat(), + "request_id": str(uuid.uuid4()), } # NEW: Invocation history endpoints