mirror of
https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal.git
synced 2026-08-29 17:34:09 +00:00
New anthropic claude service to replace openai chatgpt
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Anthropic\Services;
|
||||
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\Classes\Exceptions\MalformedRequestException;
|
||||
use App\Classes\Exceptions\ConnectionErrorException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CreatesClaudeResponse
|
||||
{
|
||||
/**
|
||||
* @param string $userPrompt
|
||||
* @param string $model
|
||||
* @return null|object
|
||||
* @throws MalformedRequestException
|
||||
*/
|
||||
public function execute(string $userPrompt, string $model = "") {
|
||||
try {
|
||||
$content = [];
|
||||
|
||||
$content[] = [
|
||||
'type' => 'text',
|
||||
'text' => $userPrompt,
|
||||
];
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'x-api-key' => config('anthropic.api_key'),
|
||||
'anthropic-version' => config('anthropic.api_version', '2023-06-01'),
|
||||
'Content-Type' => 'application/json',
|
||||
])->post(config('anthropic.base_url') . '/v1/messages', [
|
||||
'model' => config('anthropic.default_model', 'claude-sonnet-4-5-20250929'),
|
||||
'max_tokens' => 8192,
|
||||
'messages' => [
|
||||
[
|
||||
'role' => 'user',
|
||||
'content' => $content,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
} else {
|
||||
Log::info('CreatesClaudeResponse error: ' . $response->body());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (\Illuminate\Http\Client\ConnectionException $exception) {
|
||||
Log::info('ConnectionException' . json_encode($exception));
|
||||
throw new ConnectionErrorException(
|
||||
'Failed to connect to Claude API',
|
||||
$exception->getMessage(),
|
||||
$userPrompt,
|
||||
$exception->getTraceAsString()
|
||||
);
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
Log::info('Exception' . json_encode($exception));
|
||||
throw new MalformedRequestException(
|
||||
'Unable to get correct response from Claude API: ' . $exception->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Anthropic\Services;
|
||||
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\Classes\Exceptions\MalformedRequestException;
|
||||
use App\Classes\Exceptions\ConnectionErrorException;
|
||||
|
||||
class CreatesClaudeResponseWithFiles
|
||||
{
|
||||
public function execute(string $userPrompt, array $fileIds)
|
||||
{
|
||||
try {
|
||||
$content = [];
|
||||
|
||||
// User prompt
|
||||
$content[] = [
|
||||
'type' => 'text',
|
||||
'text' => $userPrompt,
|
||||
];
|
||||
|
||||
// Attach files
|
||||
foreach ($fileIds as $fileId) {
|
||||
$content[] = [
|
||||
'type' => 'document',
|
||||
'source' => [
|
||||
'type' => 'file',
|
||||
'file_id' => $fileId,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'x-api-key' => config('anthropic.api_key'),
|
||||
'anthropic-version' => config('anthropic.api_version', '2023-06-01'),
|
||||
'anthropic-beta' => 'files-api-2025-04-14',
|
||||
'Content-Type' => 'application/json',
|
||||
])->post(config('anthropic.base_url') . '/v1/messages', [
|
||||
'model' => config('anthropic.default_model', 'claude-sonnet-4-5-20250929'),
|
||||
'max_tokens' => 16384,
|
||||
'messages' => [
|
||||
[
|
||||
'role' => 'user',
|
||||
'content' => $content,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
return $response->json();
|
||||
}
|
||||
catch (\Illuminate\Http\Client\ConnectionException $exception) {
|
||||
$error = 'Failed to connect to Claude API';
|
||||
throw new ConnectionErrorException(
|
||||
$error,
|
||||
$exception->getMessage(),
|
||||
$userPrompt,
|
||||
$exception->getTraceAsString()
|
||||
);
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
throw new MalformedRequestException(
|
||||
'Unable to get correct response from Claude API: ' . $exception->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Anthropic\Services;
|
||||
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Classes\Exceptions\MalformedRequestException;
|
||||
use App\Classes\Exceptions\ConnectionErrorException;
|
||||
|
||||
class UploadsClaudeFiles
|
||||
{
|
||||
public function execute(array $paths): array
|
||||
{
|
||||
try {
|
||||
$ids = [];
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$response = Http::withHeaders([
|
||||
'x-api-key' => config('anthropic.api_key'),
|
||||
'anthropic-version' => '2023-06-01',
|
||||
'anthropic-beta' => 'files-api-2025-04-14',
|
||||
])->attach(
|
||||
'file',
|
||||
fopen($path, 'r'),
|
||||
basename($path)
|
||||
)->post(config('anthropic.base_url') . '/v1/files');
|
||||
|
||||
$data = $response->json();
|
||||
|
||||
if ($response->successful() && isset($data['id'])) {
|
||||
$ids[] = $data['id'];
|
||||
} else {
|
||||
Log::error('Claude file upload failed', [
|
||||
'path' => $path,
|
||||
'response' => $data,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
catch (\Illuminate\Http\Client\ConnectionException $exception) {
|
||||
$error = 'Failed to connect to Claude API';
|
||||
throw new ConnectionErrorException(
|
||||
$error,
|
||||
$exception->getMessage(),
|
||||
$exception->getTraceAsString()
|
||||
);
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
throw new MalformedRequestException(
|
||||
'Unable to get correct response from Claude API: ' . $exception->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
-14
@@ -3,6 +3,7 @@
|
||||
namespace App\Classes\Modules\PackingLists\Processors\V2;
|
||||
|
||||
use App\Classes\Exceptions\MalformedRequestException;
|
||||
use App\Classes\Modules\Anthropic\Services\CreatesClaudeResponse;
|
||||
use App\Classes\Modules\Orders\Services\FetchesDataFromYDPortal;
|
||||
use App\Classes\Modules\OpenAI\Services\CreatesChatGPTResponse;
|
||||
use App\Models\PackingList;
|
||||
@@ -19,14 +20,19 @@ class FetchByTrakingNoYdPortalV2Processor
|
||||
/** @var CreatesChatGPTResponse */
|
||||
private $createsChatGPTResponse;
|
||||
|
||||
/** @var CreatesClaudeResponse */
|
||||
private $createsClaudeResponse;
|
||||
|
||||
/**
|
||||
* @param FetchesDataFromYDPortal $fetchesDataFRomYDPortal
|
||||
* @param CreatesChatGPTResponse $createsChatGPTResponse
|
||||
* @param CreatesClaudeResponse $createsClaudeResponse
|
||||
*/
|
||||
public function __construct(FetchesDataFromYDPortal $fetchesDataFRomYDPortal, CreatesChatGPTResponse $createsChatGPTResponse)
|
||||
public function __construct(FetchesDataFromYDPortal $fetchesDataFRomYDPortal, CreatesChatGPTResponse $createsChatGPTResponse, CreatesClaudeResponse $createsClaudeResponse)
|
||||
{
|
||||
$this->fetchesDataFRomYDPortal = $fetchesDataFRomYDPortal;
|
||||
$this->createsChatGPTResponse = $createsChatGPTResponse;
|
||||
$this->createsClaudeResponse = $createsClaudeResponse;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,9 +135,9 @@ class FetchByTrakingNoYdPortalV2Processor
|
||||
else{
|
||||
$isUpdated = false;
|
||||
if($item['tracking']){
|
||||
$isTranslatedPreviouly = $this->checkIfTranslationAlreadyExist(!empty($item['tracking']) ? $item['tracking'] : "");
|
||||
$result = $isTranslatedPreviouly;
|
||||
if (!$isTranslatedPreviouly){
|
||||
$isTranslatedPreviously = $this->checkIfTranslationAlreadyExist(!empty($item['tracking']) ? $item['tracking'] : "");
|
||||
$result = $isTranslatedPreviously;
|
||||
if (!$isTranslatedPreviously){
|
||||
$result = $this->translateTracking($item['tracking']);
|
||||
}
|
||||
if ($result && $existingRecord->tracking_english !== $result) {
|
||||
@@ -143,9 +149,9 @@ class FetchByTrakingNoYdPortalV2Processor
|
||||
}
|
||||
|
||||
if($item['remark']) {
|
||||
$isTranslatedPreviouly = $this->checkIfTranslationAlreadyExist(!empty($item['remark']) ? $item['remark'] : "");
|
||||
$result = $isTranslatedPreviouly;
|
||||
if (!$isTranslatedPreviouly){
|
||||
$isTranslatedPreviously = $this->checkIfTranslationAlreadyExist(!empty($item['remark']) ? $item['remark'] : "");
|
||||
$result = $isTranslatedPreviously;
|
||||
if (!$isTranslatedPreviously){
|
||||
$result = $this->translateTracking($item['remark']);
|
||||
}
|
||||
|
||||
@@ -189,7 +195,7 @@ class FetchByTrakingNoYdPortalV2Processor
|
||||
$trackingDate = Carbon::parse($trackingtime);
|
||||
$trackingYear = $trackingDate->year;
|
||||
|
||||
if($remark && preg_match('/\b\d{1,2}[-\/.]\d{1,2}\b/', $remark) && $eta === null && $etd === null){
|
||||
if($remark && $eta === null && $etd === null){
|
||||
$existingRecord = YDOrderTracking::where('remark', $remark)->where(function ($query) {
|
||||
$query->whereNotNull('eta')
|
||||
->orWhereNotNull('etd');
|
||||
@@ -203,14 +209,27 @@ class FetchByTrakingNoYdPortalV2Processor
|
||||
];
|
||||
}
|
||||
|
||||
$userPrompt = "Show me the answer in json string without escape, e.g. {'ETA': '', 'ETD': ''}. ".$remark." What is the ETA and ETD?";
|
||||
$userPrompt = "You must respond with raw JSON only.
|
||||
No explanations.
|
||||
No markdown.
|
||||
No code fences.
|
||||
No comments.
|
||||
Only this JSON structure:
|
||||
{\"ETA\":\"\", \"ETD\":\"\"}
|
||||
Dates must be strings in YYYY-MM-DD format.
|
||||
|
||||
{$remark}
|
||||
What are the ETA and ETD?";
|
||||
Log::info('prompt: '.$userPrompt);
|
||||
$result = $this->createsChatGPTResponse->execute($userPrompt);
|
||||
$result = $this->createsClaudeResponse->execute($userPrompt);
|
||||
Log::info('result (json_encoded): '.json_encode($result));
|
||||
$content = $result['choices'][0]['message']['content'] ?? null;
|
||||
// $content = $result['choices'][0]['message']['content'] ?? null;
|
||||
$content = $result['content'][0]['text'] ?? '[]';
|
||||
Log::info('content (json_encoded):'. json_encode($content));
|
||||
if ($content) {
|
||||
$parsedContent = json_decode(str_replace("'", '"', $content), true);
|
||||
$parsedContent = json_decode($content, true);
|
||||
|
||||
// $parsedContent = json_decode(str_replace("'", '"', $content), true);
|
||||
if (is_array($parsedContent) && isset($parsedContent['ETA'], $parsedContent['ETD'])) {
|
||||
Log::info('parsedContent ETA:'. $parsedContent['ETA']);
|
||||
Log::info('parsedContent ETD:'. $parsedContent['ETD']);
|
||||
@@ -310,9 +329,10 @@ class FetchByTrakingNoYdPortalV2Processor
|
||||
{
|
||||
$userPrompt = "Please translate mandarin to english and return result in this format, e.g. {'mandarin': '.$textToTranslate.', 'english': ''}. ".$textToTranslate." ";
|
||||
Log::info('prompt: '.$userPrompt);
|
||||
$result = $this->createsChatGPTResponse->execute($userPrompt);
|
||||
$result = $this->createsClaudeResponse->execute($userPrompt);
|
||||
Log::info('result (json_encoded): '.json_encode($result));
|
||||
$content = $result['choices'][0]['message']['content'] ?? null;
|
||||
// $content = $result['choices'][0]['message']['content'] ?? null;
|
||||
$content = $result['content'][0]['text'] ?? '[]';
|
||||
Log::info('content (json_encoded):'. json_encode($content));
|
||||
$parsedContent = json_decode(str_replace("'", '"', $content), true);
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'base_url' => env('ANTHROPIC_BASE_URL', 'https://api.anthropic.com'),
|
||||
'api_key' => env('ANTHROPIC_API_KEY', ''),
|
||||
'api_version' => env('ANTHROPIC_API_VERSION', '2023-06-01'),
|
||||
// 'is_enabled' => env('OPENAI_IS_ENABLED', 'true'),
|
||||
];
|
||||
Reference in New Issue
Block a user