Skip to content
Merged
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
8 changes: 8 additions & 0 deletions src/Agents/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ abstract public function setModel(string $model): self;
*/
abstract public function isSchemaSupported(): bool;

/**
* Format error message
*
* @param mixed $json
* @return string
*/
abstract protected function formatErrorMessage($json): string;

/**
* Get the current agent
*
Expand Down
38 changes: 28 additions & 10 deletions src/Agents/Adapters/Anthropic.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ function ($chunk) use (&$content, $listener) {
}

if ($response->getStatusCode() >= 400) {
if (! $payload['stream']) {
$responseBody = $response->getBody();
$json = is_string($responseBody) ? json_decode($responseBody, true) : null;
$content = $this->formatErrorMessage($json);
}

throw new \Exception(
ucfirst($this->getName()).' API error: '.$content,
$response->getStatusCode()
Expand Down Expand Up @@ -276,15 +282,10 @@ protected function process(Chunk $chunk, ?callable $listener): string
continue;
}

$json = json_decode($line, true);
if (is_array($json) && isset($json['type']) && $json['type'] === 'error') {
$type = $json['error']['type'] ?? '';
$message = $json['error']['message'] ?? 'Unknown error';

return '('.$type.') '.$message;
}
// Check if line starts with "data: " prefix and remove it, otherwise use the line as-is
$jsonString = str_starts_with($line, 'data: ') ? substr($line, 6) : $line;
$json = json_decode($jsonString, true);

$json = json_decode(substr($line, 6), true);
if (! is_array($json)) {
continue;
}
Expand Down Expand Up @@ -355,8 +356,7 @@ protected function process(Chunk $chunk, ?callable $listener): string
break;

case 'error':
$errorMessage = isset($json['error']['message']) ? (string) $json['error']['message'] : 'Unknown error';
throw new \Exception('Anthropic API error: '.$errorMessage);
return $this->formatErrorMessage($json);
}
}

Expand Down Expand Up @@ -439,4 +439,22 @@ public function getName(): string
{
return 'anthropic';
}

/**
* Extract and format error information from API response
*
* @param mixed $json
* @return string
*/
protected function formatErrorMessage($json): string
{
if (! is_array($json)) {
return '(unknown_error) Unknown error';
}

$errorType = isset($json['error']['type']) ? (string) $json['error']['type'] : 'unknown_error';
$errorMessage = isset($json['error']['message']) ? (string) $json['error']['message'] : 'Unknown error';

return '('.$errorType.') '.$errorMessage;
}
}
23 changes: 19 additions & 4 deletions src/Agents/Adapters/Deepseek.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,7 @@ protected function process(Chunk $chunk, ?callable $listener): string

$json = json_decode($data, true);
if (is_array($json) && isset($json['error'])) {
$type = $json['error']['type'] ?? '';
$message = $json['error']['message'] ?? 'Unknown error';

return '('.$type.') '.$message;
return $this->formatErrorMessage($json);
}

foreach ($lines as $line) {
Expand Down Expand Up @@ -302,4 +299,22 @@ public function getName(): string
{
return 'deepseek';
}

/**
* Extract and format error information from API response
*
* @param mixed $json
* @return string
*/
protected function formatErrorMessage($json): string
{
if (! is_array($json)) {
return '(unknown_error) Unknown error';
}

$errorType = isset($json['error']['type']) ? (string) $json['error']['type'] : 'unknown_error';
$errorMessage = isset($json['error']['message']) ? (string) $json['error']['message'] : 'Unknown error';

return '('.$errorType.') '.$errorMessage;
}
}
26 changes: 20 additions & 6 deletions src/Agents/Adapters/Gemini.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,7 @@ protected function process(Chunk $chunk, ?callable $listener): string

$json = json_decode($data, true);
if (is_array($json) && isset($json['error'])) {
$error = '('.($json['error']['status'] ?? '').') '.($json['error']['message'] ?? 'Unknown error');
if (! empty($json['error']['details'])) {
$error .= PHP_EOL.json_encode($json['error']['details'], JSON_PRETTY_PRINT);
}

return $error;
return $this->formatErrorMessage($json);
}

foreach ($lines as $line) {
Expand Down Expand Up @@ -334,4 +329,23 @@ public function getName(): string
{
return 'gemini';
}

/**
* Extract and format error information from API response
*
* @param mixed $json
* @return string
*/
protected function formatErrorMessage($json): string
{
if (! is_array($json)) {
return '(unknown_error) Unknown error';
}

$errorType = isset($json['error']['status']) ? (string) $json['error']['status'] : 'unknown_error';
$errorMessage = isset($json['error']['message']) ? (string) $json['error']['message'] : 'Unknown error';
$errorDetails = isset($json['error']['details']) ? json_encode($json['error']['details'], JSON_PRETTY_PRINT) : '';

return '('.$errorType.') '.$errorMessage.PHP_EOL.$errorDetails;
}
}
34 changes: 29 additions & 5 deletions src/Agents/Adapters/OpenAI.php
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ function ($chunk) use (&$content, $listener) {
$payload,
);
$body = $response->getBody();

if ($response->getStatusCode() >= 400) {
$json = is_string($body) ? json_decode($body, true) : null;
$content = $this->formatErrorMessage($json);
throw new \Exception(
ucfirst($this->getName()).' API error: '.$content,
$response->getStatusCode()
);
}

$json = is_string($body) ? json_decode($body, true) : null;
if (is_array($json) && isset($json['choices'][0]['message']['content'])) {
$content = $json['choices'][0]['message']['content'];
Expand Down Expand Up @@ -253,11 +263,7 @@ protected function process(Chunk $chunk, ?callable $listener): string

$json = json_decode($data, true);
if (is_array($json) && isset($json['error'])) {
if (isset($json['error']['code'], $json['error']['message'])) {
return '('.$json['error']['code'].') '.$json['error']['message'];
}

return is_array($json['error']) ? json_encode($json['error']) : $json['error'];
return $this->formatErrorMessage($json);
}

foreach ($lines as $line) {
Expand Down Expand Up @@ -390,4 +396,22 @@ public function getName(): string
{
return 'openai';
}

/**
* Extract and format error information from API response
*
* @param mixed $json
* @return string
*/
protected function formatErrorMessage($json): string
{
if (! is_array($json)) {
return '(unknown_error) Unknown error';
}

$errorType = isset($json['error']['code']) ? (string) $json['error']['code'] : 'unknown_error';
$errorMessage = isset($json['error']['message']) ? (string) $json['error']['message'] : 'Unknown error';

return '('.$errorType.') '.$errorMessage;
}
}
8 changes: 2 additions & 6 deletions src/Agents/Adapters/Perplexity.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,8 @@ protected function process(Chunk $chunk, ?callable $listener): string
$lines = explode("\n", $data);

$json = json_decode($data, true);
if (is_array($json)) {
if (isset($json['error']['code'], $json['error']['message'])) {
return '('.$json['error']['code'].') '.$json['error']['message'];
}

return $json['error'] ?? 'Unknown error';
if (is_array($json) && isset($json['error'])) {
return $this->formatErrorMessage($json);
}

// Specifically for Authorization and similar errors that return HTML
Expand Down
72 changes: 72 additions & 0 deletions src/Agents/Adapters/XAI.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Utopia\Agents\Adapters;

use Utopia\Fetch\Chunk;

class XAI extends OpenAI
{
/**
Expand Down Expand Up @@ -81,4 +83,74 @@ public function getName(): string
{
return 'xai';
}

/**
* Process a stream chunk from the OpenAI API
*
* @param \Utopia\Fetch\Chunk $chunk
* @param callable|null $listener
* @return string
*
* @throws \Exception
*/
protected function process(Chunk $chunk, ?callable $listener): string
{
$block = '';
$data = $chunk->getData();
$lines = explode("\n", $data);

$json = json_decode($data, true);
if (is_array($json) && isset($json['error'])) {
return $this->formatErrorMessage($json);
}

foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}

if (! str_starts_with($line, 'data: ')) {
continue;
}

// Handle [DONE] message
if (trim($line) === 'data: [DONE]') {
continue;
}

$json = json_decode(substr($line, 6), true);
if (! is_array($json)) {
continue;
}

// Extract content from the choices array
if (isset($json['choices'][0]['delta']['content'])) {
$block = $json['choices'][0]['delta']['content'];

if (! empty($block) && $listener !== null) {
$listener($block);
}
}
}

return $block;
}

/**
* Extract and format error information from API response
*
* @param mixed $json
* @return string
*/
protected function formatErrorMessage($json): string
{
if (! is_array($json)) {
return '(unknown_error) Unknown error';
}

$errorType = isset($json['code']) ? (string) $json['code'] : 'unknown_error';
$errorMessage = isset($json['error']) ? (string) $json['error'] : 'Unknown error';

return '('.$errorType.') '.$errorMessage;
}
}