├── .env.example ├── phpstan.neon ├── examples ├── shared │ └── fixtures │ │ └── voice.mp3 ├── .env.example └── composer.json ├── .claude └── settings.local.json ├── src ├── Enums │ ├── Source.php │ ├── FilePurpose.php │ ├── SampleType.php │ ├── TimestampGranularity.php │ ├── Role.php │ ├── EntityType.php │ ├── ResponseFormat.php │ ├── AccessRole.php │ ├── DocumentStatus.php │ ├── BatchJobStatus.php │ ├── JobStatus.php │ └── Model.php ├── Dto │ ├── Files │ │ ├── FileSignedURL.php │ │ ├── UploadFileOut.php │ │ ├── DeleteFileOut.php │ │ ├── ListFilesOut.php │ │ ├── RetrieveFileOut.php │ │ ├── FileObject.php │ │ └── ListFilesRequest.php │ ├── FineTuning │ │ ├── ValidationError.php │ │ ├── UpdateFTModelIn.php │ │ ├── WandbIntegration.php │ │ ├── TrainingFile.php │ │ ├── Checkpoint.php │ │ ├── ArchiveFTModelOut.php │ │ ├── UnarchiveFTModelOut.php │ │ ├── CheckpointMetrics.php │ │ ├── ClassifierFTModelOut.php │ │ ├── CompletionFTModelOut.php │ │ ├── TrainingParameters.php │ │ ├── JobIn.php │ │ ├── LegacyJobMetadataOut.php │ │ ├── JobMetadata.php │ │ ├── JobsOut.php │ │ ├── ClassifierJobOut.php │ │ ├── CompletionJobOut.php │ │ ├── ClassifierDetailedJobOut.php │ │ └── CompletionDetailedJobOut.php │ ├── Fim │ │ ├── StreamedFIMDelta.php │ │ ├── FIMChoice.php │ │ ├── StreamedFIMChoice.php │ │ ├── FIMCompletionResponse.php │ │ ├── StreamedFIMCompletionResponse.php │ │ └── FIMCompletionRequest.php │ ├── Libraries │ │ ├── DocumentUpdateIn.php │ │ ├── LibraryIn.php │ │ ├── LibraryInUpdate.php │ │ ├── SharingDelete.php │ │ ├── LibraryOut.php │ │ ├── ListSharingOut.php │ │ ├── SharingIn.php │ │ ├── ListLibraryOut.php │ │ ├── ListDocumentOut.php │ │ ├── SharingOut.php │ │ └── DocumentOut.php │ ├── OCR │ │ ├── Dimensions.php │ │ ├── UsageInfo.php │ │ ├── Page.php │ │ ├── OCRResponse.php │ │ ├── Image.php │ │ ├── OCRRequest.php │ │ └── Document.php │ ├── Audio │ │ ├── TranscriptionWord.php │ │ ├── TranscriptionSegment.php │ │ └── TranscriptionResponse.php │ ├── Chat │ │ ├── StreamedChatCompletionDelta.php │ │ ├── ToolCalls.php │ │ ├── FunctionCall.php │ │ ├── ChatCompletionChoice.php │ │ ├── StreamedChatCompletionChoice.php │ │ ├── ChatCompletionMessage.php │ │ ├── ChatCompletionResponse.php │ │ ├── StreamedChatCompletionResponse.php │ │ └── ChatCompletionRequest.php │ ├── Moderations │ │ ├── ChatModerationRequest.php │ │ ├── ModerationResponse.php │ │ ├── ModerationCategories.php │ │ ├── ModerationCategoryScores.php │ │ └── ModerationResult.php │ ├── Classifications │ │ ├── ClassificationRequest.php │ │ ├── ChatClassificationRequest.php │ │ ├── ClassificationResult.php │ │ └── ClassificationResponse.php │ ├── Embedding │ │ ├── Embedding.php │ │ ├── EmbeddingRequest.php │ │ └── EmbeddingResponse.php │ ├── Models │ │ ├── DeleteModelOut.php │ │ ├── ModelList.php │ │ ├── Model.php │ │ ├── ModelCapabilities.php │ │ ├── ModelPermission.php │ │ ├── BaseModelCard.php │ │ └── FTModelCard.php │ ├── Conversations │ │ ├── ConversationHistory.php │ │ ├── ConversationMessages.php │ │ ├── ModelConversation.php │ │ ├── AgentConversation.php │ │ ├── ConversationList.php │ │ ├── ConversationResponse.php │ │ ├── ConversationMessage.php │ │ ├── ConversationEntry.php │ │ ├── ConversationAppendRequest.php │ │ ├── ConversationRestartRequest.php │ │ └── ConversationRequest.php │ ├── Agents │ │ ├── AgentList.php │ │ ├── AgentCreationRequest.php │ │ ├── AgentUpdateRequest.php │ │ └── Agent.php │ ├── Usage.php │ ├── Batch │ │ ├── BatchJobsOut.php │ │ ├── BatchJobIn.php │ │ └── BatchJobOut.php │ └── SimpleChat │ │ ├── SimpleStreamChunk.php │ │ └── SimpleChatResponse.php ├── Facades │ └── Mistral.php ├── Requests │ ├── Conversations │ │ ├── GetConversationRequest.php │ │ ├── GetConversationHistoryRequest.php │ │ ├── GetConversationMessagesRequest.php │ │ ├── CreateConversationStreamRequest.php │ │ ├── AppendToConversationStreamRequest.php │ │ ├── RestartConversationStreamRequest.php │ │ ├── ListConversationsRequest.php │ │ ├── CreateConversationRequest.php │ │ ├── AppendToConversationRequest.php │ │ └── RestartConversationRequest.php │ ├── Libraries │ │ ├── DeleteLibrary.php │ │ ├── DeleteDocument.php │ │ ├── GetLibrary.php │ │ ├── ListSharing.php │ │ ├── GetDocument.php │ │ ├── DeleteSharing.php │ │ ├── CreateLibrary.php │ │ ├── ListLibraries.php │ │ ├── CreateSharing.php │ │ ├── UpdateLibrary.php │ │ ├── UpdateDocument.php │ │ ├── ListDocuments.php │ │ └── UploadDocument.php │ ├── FineTuning │ │ ├── GetJobRequest.php │ │ ├── CancelJobRequest.php │ │ ├── StartJobRequest.php │ │ ├── ArchiveModelRequest.php │ │ ├── UnarchiveModelRequest.php │ │ ├── UpdateModelRequest.php │ │ ├── CreateJobRequest.php │ │ └── ListJobsRequest.php │ ├── Files │ │ ├── DownloadFile.php │ │ ├── DeleteFile.php │ │ ├── RetrieveFile.php │ │ ├── ListFiles.php │ │ ├── GetSignedUrl.php │ │ └── UploadFile.php │ ├── Models │ │ ├── ListModels.php │ │ ├── DeleteModelRequest.php │ │ └── RetrieveModelRequest.php │ ├── Agents │ │ ├── GetAgentRequest.php │ │ ├── UpdateAgentVersionRequest.php │ │ ├── ListAgentsRequest.php │ │ ├── CreateAgentRequest.php │ │ └── UpdateAgentRequest.php │ ├── Batch │ │ ├── GetBatchJobRequest.php │ │ ├── CancelBatchJobRequest.php │ │ ├── CreateBatchJobRequest.php │ │ └── ListBatchJobsRequest.php │ ├── OCR │ │ └── ProcessDocument.php │ ├── Embedding │ │ └── CreateEmbedding.php │ ├── Chat │ │ └── CreateChatCompletion.php │ ├── Classifications │ │ ├── CreateModerationRequest.php │ │ ├── CreateChatModerationRequest.php │ │ ├── CreateClassificationRequest.php │ │ └── CreateChatClassificationRequest.php │ ├── Fim │ │ └── CreateFIMCompletionRequest.php │ └── Audio │ │ ├── CreateTranscriptionStreamRequest.php │ │ └── CreateTranscriptionRequest.php ├── MistralServiceProvider.php ├── Resource │ ├── Embedding.php │ ├── Models.php │ ├── Agents.php │ ├── Classifications.php │ ├── Fim.php │ ├── Batch.php │ ├── Audio.php │ ├── SimpleChat.php │ ├── Libraries.php │ ├── OCR.php │ └── Chat.php ├── Concerns │ └── HandlesStreamedResponses.php └── Mistral.php ├── LICENSE.md ├── config └── mistral.php ├── justfile ├── composer.json └── CLAUDE.md /.env.example: -------------------------------------------------------------------------------- 1 | MISTRAL_API_KEY="replace-me" 2 | MISTRAL_BASE_URL= 3 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 5 3 | paths: 4 | - src 5 | -------------------------------------------------------------------------------- /examples/shared/fixtures/voice.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelgeSverre/mistral/HEAD/examples/shared/fixtures/voice.mp3 -------------------------------------------------------------------------------- /.claude/settings.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "allow": [ 4 | "Bash(git commit:*)" 5 | ], 6 | "deny": [], 7 | "ask": [] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Enums/Source.php: -------------------------------------------------------------------------------- 1 | arguments, true); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Dto/Fim/FIMChoice.php: -------------------------------------------------------------------------------- 1 | conversationId}"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Dto/Libraries/SharingDelete.php: -------------------------------------------------------------------------------- 1 | libraryId}"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Dto/Classifications/ClassificationResponse.php: -------------------------------------------------------------------------------- 1 | jobId}"; 18 | } 19 | 20 | public function __construct( 21 | protected string $jobId, 22 | ) {} 23 | } 24 | -------------------------------------------------------------------------------- /src/Requests/FineTuning/CancelJobRequest.php: -------------------------------------------------------------------------------- 1 | jobId}/cancel"; 18 | } 19 | 20 | public function __construct( 21 | protected string $jobId, 22 | ) {} 23 | } 24 | -------------------------------------------------------------------------------- /src/Dto/FineTuning/CheckpointMetrics.php: -------------------------------------------------------------------------------- 1 | jobId}/start"; 18 | } 19 | 20 | public function __construct( 21 | protected string $jobId, 22 | ) {} 23 | } 24 | -------------------------------------------------------------------------------- /src/Dto/Conversations/AgentConversation.php: -------------------------------------------------------------------------------- 1 | $data 13 | */ 14 | public function __construct( 15 | #[DataCollectionOf(BatchJobOut::class)] 16 | public DataCollection $data, 17 | public int $total, 18 | public string $object = 'list', 19 | ) {} 20 | } 21 | -------------------------------------------------------------------------------- /src/Requests/Files/DownloadFile.php: -------------------------------------------------------------------------------- 1 | fileId}/content"; 20 | } 21 | 22 | public function __construct(protected readonly string $fileId) {} 23 | } 24 | -------------------------------------------------------------------------------- /examples/.env.example: -------------------------------------------------------------------------------- 1 | # Mistral API Configuration 2 | # Get your API key from https://console.mistral.ai/ 3 | MISTRAL_API_KEY=your_api_key_here 4 | 5 | # Optional: Custom API endpoint (default: https://api.mistral.ai/v1) 6 | # MISTRAL_BASE_URL=https://api.mistral.ai/v1 7 | 8 | # Optional: Request timeout in seconds (default: 60) 9 | # MISTRAL_TIMEOUT=60 10 | 11 | # Optional: Enable debug logging in error handlers (true/false, default: false) 12 | # DEBUG_MODE=false 13 | 14 | # Optional: Log file path for debug output (default: /tmp/mistral-examples.log) 15 | # LOG_FILE=/tmp/mistral-examples.log -------------------------------------------------------------------------------- /src/Dto/Libraries/ListSharingOut.php: -------------------------------------------------------------------------------- 1 | $data 15 | */ 16 | public function __construct( 17 | #[DataCollectionOf(SharingOut::class)] 18 | public DataCollection $data, 19 | public ?string $object = null, 20 | ) {} 21 | } 22 | -------------------------------------------------------------------------------- /src/Dto/Libraries/SharingIn.php: -------------------------------------------------------------------------------- 1 | libraryId}/documents/{$this->documentId}"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Dto/Agents/AgentCreationRequest.php: -------------------------------------------------------------------------------- 1 | $data 15 | */ 16 | public function __construct( 17 | #[DataCollectionOf(LibraryOut::class)] 18 | public DataCollection $data, 19 | public ?string $object = null, 20 | public ?int $total = null, 21 | ) {} 22 | } 23 | -------------------------------------------------------------------------------- /src/Dto/Libraries/ListDocumentOut.php: -------------------------------------------------------------------------------- 1 | $data 15 | */ 16 | public function __construct( 17 | #[DataCollectionOf(DocumentOut::class)] 18 | public DataCollection $data, 19 | public ?string $object = null, 20 | public ?int $total = null, 21 | ) {} 22 | } 23 | -------------------------------------------------------------------------------- /src/Dto/Embedding/EmbeddingResponse.php: -------------------------------------------------------------------------------- 1 | json()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Dto/Chat/StreamedChatCompletionResponse.php: -------------------------------------------------------------------------------- 1 | agentId}"; 24 | } 25 | 26 | public function createDtoFromResponse(Response $response): Agent 27 | { 28 | return Agent::from($response->json()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Dto/OCR/Image.php: -------------------------------------------------------------------------------- 1 | libraryId}"; 23 | } 24 | 25 | public function createDtoFromResponse(Response $response): LibraryOut 26 | { 27 | return LibraryOut::from($response->json()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/MistralServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('mistral')->hasConfigFile(); 13 | } 14 | 15 | public function packageBooted(): void 16 | { 17 | $this->app->bind(Mistral::class, function () { 18 | return new Mistral( 19 | apiKey: config('mistral.api_key'), 20 | baseUrl: config('mistral.base_url'), 21 | timeout: config('mistral.timeout', 30), 22 | ); 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Dto/Conversations/ConversationEntry.php: -------------------------------------------------------------------------------- 1 | jobId}"; 20 | } 21 | 22 | public function __construct( 23 | protected string $jobId, 24 | ) {} 25 | 26 | public function createDtoFromResponse(Response $response): BatchJobOut 27 | { 28 | return BatchJobOut::from($response->json()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Requests/Files/DeleteFile.php: -------------------------------------------------------------------------------- 1 | fileId}"; 22 | } 23 | 24 | public function __construct(protected readonly string $fileId) {} 25 | 26 | public function createDtoFromResponse(Response $response): DeleteFileOut 27 | { 28 | return DeleteFileOut::from($response->json()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Dto/Batch/BatchJobIn.php: -------------------------------------------------------------------------------- 1 | jobId}/cancel"; 20 | } 21 | 22 | public function __construct( 23 | protected string $jobId, 24 | ) {} 25 | 26 | public function createDtoFromResponse(Response $response): BatchJobOut 27 | { 28 | return BatchJobOut::from($response->json()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Requests/Libraries/ListSharing.php: -------------------------------------------------------------------------------- 1 | libraryId}/share"; 23 | } 24 | 25 | public function createDtoFromResponse(Response $response): ListSharingOut 26 | { 27 | return ListSharingOut::from($response->json()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Requests/Files/RetrieveFile.php: -------------------------------------------------------------------------------- 1 | fileId}"; 22 | } 23 | 24 | public function __construct(protected readonly string $fileId) {} 25 | 26 | public function createDtoFromResponse(Response $response): RetrieveFileOut 27 | { 28 | return RetrieveFileOut::from($response->json()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Dto/Audio/TranscriptionSegment.php: -------------------------------------------------------------------------------- 1 | */ 17 | public array $tokens, 18 | public float $temperature, 19 | #[MapName('avg_logprob')] 20 | public float $avgLogprob, 21 | #[MapName('compression_ratio')] 22 | public float $compressionRatio, 23 | #[MapName('no_speech_prob')] 24 | public float $noSpeechProb, 25 | ) {} 26 | } 27 | -------------------------------------------------------------------------------- /src/Requests/Conversations/GetConversationHistoryRequest.php: -------------------------------------------------------------------------------- 1 | conversationId}/history"; 19 | } 20 | 21 | public function createDtoFromResponse(Response $response): ConversationHistory 22 | { 23 | return ConversationHistory::from($response->json()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Dto/SimpleChat/SimpleStreamChunk.php: -------------------------------------------------------------------------------- 1 | conversationId}/messages"; 19 | } 20 | 21 | public function createDtoFromResponse(Response $response): ConversationMessages 22 | { 23 | return ConversationMessages::from($response->json()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Dto/Models/ModelCapabilities.php: -------------------------------------------------------------------------------- 1 | $this->model, 21 | 'document' => $this->document->toArray(), 22 | ]; 23 | 24 | if ($this->includeImageBase64 !== null) { 25 | $data['include_image_base64'] = $this->includeImageBase64; 26 | } 27 | 28 | return $data; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Requests/Models/DeleteModelRequest.php: -------------------------------------------------------------------------------- 1 | modelId; 26 | } 27 | 28 | public function createDtoFromResponse(Response $response): DeleteModelOut 29 | { 30 | return DeleteModelOut::from($response->json()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Requests/FineTuning/ArchiveModelRequest.php: -------------------------------------------------------------------------------- 1 | modelId}/archive"; 20 | } 21 | 22 | public function __construct( 23 | protected string $modelId, 24 | ) {} 25 | 26 | public function createDtoFromResponse(Response $response): ArchiveFTModelOut 27 | { 28 | return ArchiveFTModelOut::from($response->json()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Dto/FineTuning/TrainingParameters.php: -------------------------------------------------------------------------------- 1 | libraryId}/documents/{$this->documentId}"; 24 | } 25 | 26 | public function createDtoFromResponse(Response $response): DocumentOut 27 | { 28 | return DocumentOut::from($response->json()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Requests/FineTuning/UnarchiveModelRequest.php: -------------------------------------------------------------------------------- 1 | modelId}/archive"; 20 | } 21 | 22 | public function __construct( 23 | protected string $modelId, 24 | ) {} 25 | 26 | public function createDtoFromResponse(Response $response): UnarchiveFTModelOut 27 | { 28 | return UnarchiveFTModelOut::from($response->json()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Dto/FineTuning/JobIn.php: -------------------------------------------------------------------------------- 1 | libraryId}/share"; 27 | } 28 | 29 | protected function defaultBody(): array 30 | { 31 | return $this->sharing->toArray(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Dto/FineTuning/JobMetadata.php: -------------------------------------------------------------------------------- 1 | conversationRequest->toArray()); 27 | $body['stream'] = true; 28 | 29 | return $body; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Requests/FineTuning/UpdateModelRequest.php: -------------------------------------------------------------------------------- 1 | modelId}"; 23 | } 24 | 25 | public function __construct( 26 | protected string $modelId, 27 | protected UpdateFTModelIn $updateModel, 28 | ) {} 29 | 30 | protected function defaultBody(): array 31 | { 32 | return array_filter($this->updateModel->toArray()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Requests/Files/ListFiles.php: -------------------------------------------------------------------------------- 1 | listFilesRequest->toArray(); 30 | } 31 | 32 | public function createDtoFromResponse(Response $response): ListFilesOut 33 | { 34 | return ListFilesOut::from($response->json()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Requests/Agents/UpdateAgentVersionRequest.php: -------------------------------------------------------------------------------- 1 | agentId}/version"; 27 | } 28 | 29 | protected function defaultQuery(): array 30 | { 31 | return [ 32 | 'version' => $this->version, 33 | ]; 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): Agent 37 | { 38 | return Agent::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Requests/Conversations/AppendToConversationStreamRequest.php: -------------------------------------------------------------------------------- 1 | conversationId}"; 25 | } 26 | 27 | protected function defaultBody(): array 28 | { 29 | $body = array_filter($this->appendRequest->toArray()); 30 | $body['stream'] = true; 31 | 32 | return $body; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Requests/Conversations/RestartConversationStreamRequest.php: -------------------------------------------------------------------------------- 1 | conversationId}/restart"; 25 | } 26 | 27 | protected function defaultBody(): array 28 | { 29 | $body = array_filter($this->restartRequest->toArray()); 30 | $body['stream'] = true; 31 | 32 | return $body; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Requests/Libraries/CreateLibrary.php: -------------------------------------------------------------------------------- 1 | library->toArray(); 33 | } 34 | 35 | public function createDtoFromResponse(Response $response): LibraryOut 36 | { 37 | return LibraryOut::from($response->json()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Requests/Libraries/ListLibraries.php: -------------------------------------------------------------------------------- 1 | $this->page, 30 | 'page_size' => $this->pageSize, 31 | ], fn ($value) => $value !== null); 32 | } 33 | 34 | public function createDtoFromResponse(Response $response): ListLibraryOut 35 | { 36 | return ListLibraryOut::from($response->json()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Dto/FineTuning/JobsOut.php: -------------------------------------------------------------------------------- 1 | $data 11 | */ 12 | public function __construct( 13 | public array $data, 14 | public string $object = 'list', 15 | public ?int $total = null, 16 | ) {} 17 | 18 | public static function fromArray(array $data): self 19 | { 20 | $jobs = []; 21 | foreach ($data['data'] as $jobData) { 22 | $jobs[] = match ($jobData['job_type'] ?? '') { 23 | 'completion' => CompletionJobOut::from($jobData), 24 | 'classifier' => ClassifierJobOut::from($jobData), 25 | default => CompletionJobOut::from($jobData), 26 | }; 27 | } 28 | 29 | return new self( 30 | data: $jobs, 31 | object: $data['object'] ?? 'list', 32 | total: $data['total'] ?? null, 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Requests/OCR/ProcessDocument.php: -------------------------------------------------------------------------------- 1 | ocrRequest->toArray()); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): OCRResponse 37 | { 38 | return OCRResponse::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Requests/Batch/CreateBatchJobRequest.php: -------------------------------------------------------------------------------- 1 | batchJobIn->toArray(); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): BatchJobOut 37 | { 38 | return BatchJobOut::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Requests/FineTuning/CreateJobRequest.php: -------------------------------------------------------------------------------- 1 | jobIn->toArray()); 33 | } 34 | 35 | protected function defaultQuery(): array 36 | { 37 | return array_filter([ 38 | 'dry_run' => $this->dryRun, 39 | ], fn ($value) => $value !== null); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Requests/Agents/ListAgentsRequest.php: -------------------------------------------------------------------------------- 1 | $this->page, 33 | 'page_size' => $this->pageSize, 34 | ], fn ($value) => $value !== null); 35 | } 36 | 37 | public function createDtoFromResponse(Response $response): AgentList 38 | { 39 | return AgentList::from($response->json()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Requests/Files/GetSignedUrl.php: -------------------------------------------------------------------------------- 1 | fileId}/url"; 22 | } 23 | 24 | public function __construct( 25 | protected readonly string $fileId, 26 | protected readonly ?int $expiry = null 27 | ) {} 28 | 29 | protected function defaultQuery(): array 30 | { 31 | if ($this->expiry === null) { 32 | return []; 33 | } 34 | 35 | return ['expiry' => $this->expiry]; 36 | } 37 | 38 | public function createDtoFromResponse(Response $response): FileSignedURL 39 | { 40 | return FileSignedURL::from($response->json()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Requests/Conversations/ListConversationsRequest.php: -------------------------------------------------------------------------------- 1 | $this->page, 29 | 'page_size' => $this->pageSize, 30 | 'order' => $this->order, 31 | ]); 32 | } 33 | 34 | public function createDtoFromResponse(Response $response): ConversationList 35 | { 36 | return ConversationList::from($response->json()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Requests/Conversations/CreateConversationRequest.php: -------------------------------------------------------------------------------- 1 | conversationRequest->toArray()); 29 | } 30 | 31 | public function createDtoFromResponse(Response $response): ConversationResponse 32 | { 33 | return ConversationResponse::from($response->json()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Requests/Embedding/CreateEmbedding.php: -------------------------------------------------------------------------------- 1 | embeddingRequest->toArray(); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): EmbeddingResponse 37 | { 38 | return EmbeddingResponse::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Requests/Libraries/CreateSharing.php: -------------------------------------------------------------------------------- 1 | libraryId}/share"; 29 | } 30 | 31 | protected function defaultBody(): array 32 | { 33 | return $this->sharing->toArray(); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): SharingOut 37 | { 38 | return SharingOut::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Requests/Models/RetrieveModelRequest.php: -------------------------------------------------------------------------------- 1 | modelId; 27 | } 28 | 29 | public function createDtoFromResponse(Response $response): BaseModelCard|FTModelCard 30 | { 31 | $data = $response->json(); 32 | 33 | // Discriminate based on the 'type' field 34 | if (isset($data['type']) && $data['type'] === 'fine-tuned') { 35 | return FTModelCard::from($data); 36 | } 37 | 38 | return BaseModelCard::from($data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Requests/Agents/CreateAgentRequest.php: -------------------------------------------------------------------------------- 1 | agentCreationRequest->toArray(); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): Agent 37 | { 38 | return Agent::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Requests/Libraries/UpdateLibrary.php: -------------------------------------------------------------------------------- 1 | libraryId}"; 29 | } 30 | 31 | protected function defaultBody(): array 32 | { 33 | return $this->library->toArray(); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): LibraryOut 37 | { 38 | return LibraryOut::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Dto/Models/ModelPermission.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Dto/Models/BaseModelCard.php: -------------------------------------------------------------------------------- 1 | */ 25 | public array $aliases = [], 26 | public ?string $deprecation = null, 27 | #[MapName('deprecation_replacement_model')] 28 | public ?string $deprecationReplacementModel = null, 29 | #[MapName('default_model_temperature')] 30 | public ?float $defaultModelTemperature = null, 31 | public string $type = 'base', 32 | ) {} 33 | } 34 | -------------------------------------------------------------------------------- /src/Requests/Chat/CreateChatCompletion.php: -------------------------------------------------------------------------------- 1 | chatCompletionRequest->toArray()); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): ChatCompletionResponse 37 | { 38 | return ChatCompletionResponse::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Requests/Classifications/CreateModerationRequest.php: -------------------------------------------------------------------------------- 1 | classificationRequest->toArray(); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): ModerationResponse 37 | { 38 | return ModerationResponse::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Dto/Files/ListFilesRequest.php: -------------------------------------------------------------------------------- 1 | $this->page, 28 | 'page_size' => $this->pageSize, 29 | 'sample_type' => $this->sampleType?->value, 30 | 'source' => $this->source?->value, 31 | 'search' => $this->search, 32 | 'purpose' => $this->purpose?->value, 33 | ], fn ($value) => $value !== null); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Requests/Agents/UpdateAgentRequest.php: -------------------------------------------------------------------------------- 1 | agentId}"; 32 | } 33 | 34 | protected function defaultBody(): array 35 | { 36 | return $this->agentUpdateRequest->toArray(); 37 | } 38 | 39 | public function createDtoFromResponse(Response $response): Agent 40 | { 41 | return Agent::from($response->json()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Requests/Libraries/UpdateDocument.php: -------------------------------------------------------------------------------- 1 | libraryId}/documents/{$this->documentId}"; 30 | } 31 | 32 | protected function defaultBody(): array 33 | { 34 | return $this->update->toArray(); 35 | } 36 | 37 | public function createDtoFromResponse(Response $response): DocumentOut 38 | { 39 | return DocumentOut::from($response->json()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Requests/Fim/CreateFIMCompletionRequest.php: -------------------------------------------------------------------------------- 1 | fimCompletionRequest->toArray()); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): FIMCompletionResponse 37 | { 38 | return FIMCompletionResponse::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Requests/Classifications/CreateChatModerationRequest.php: -------------------------------------------------------------------------------- 1 | chatModerationRequest->toArray(); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): ModerationResponse 37 | { 38 | return ModerationResponse::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Requests/Classifications/CreateClassificationRequest.php: -------------------------------------------------------------------------------- 1 | classificationRequest->toArray(); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): ClassificationResponse 37 | { 38 | return ClassificationResponse::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Requests/Libraries/ListDocuments.php: -------------------------------------------------------------------------------- 1 | libraryId}/documents"; 26 | } 27 | 28 | protected function defaultQuery(): array 29 | { 30 | return array_filter([ 31 | 'search' => $this->search, 32 | 'page' => $this->page, 33 | 'page_size' => $this->pageSize, 34 | ], fn ($value) => $value !== null); 35 | } 36 | 37 | public function createDtoFromResponse(Response $response): ListDocumentOut 38 | { 39 | return ListDocumentOut::from($response->json()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Resource/Embedding.php: -------------------------------------------------------------------------------- 1 | value, 17 | string $encodingFormat = 'float' 18 | ): Response { 19 | return $this->connector->send(new CreateEmbedding( 20 | new EmbeddingRequest( 21 | model: $model, 22 | input: $input, 23 | encodingFormat: $encodingFormat, 24 | ) 25 | )); 26 | } 27 | 28 | /** 29 | * Create embeddings and return typed DTO 30 | */ 31 | public function createDto( 32 | array $input, 33 | string $model = Model::embed->value, 34 | string $encodingFormat = 'float' 35 | ): EmbeddingResponse { 36 | return $this->create($input, $model, $encodingFormat)->dto(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helgesverre/mistral-php-examples", 3 | "description": "Example applications for Mistral PHP SDK", 4 | "type": "project", 5 | "require": { 6 | "php": "^8.1", 7 | "helgesverre/mistral": "@dev", 8 | "vlucas/phpdotenv": "^5.5", 9 | "symfony/var-dumper": "^6.0 || ^7.0" 10 | }, 11 | "require-dev": { 12 | "pestphp/pest": "^2.0" 13 | }, 14 | "autoload": { 15 | "files": [ 16 | "shared/helpers.php" 17 | ] 18 | }, 19 | "config": { 20 | "optimize-autoloader": true, 21 | "preferred-install": "dist", 22 | "sort-packages": true, 23 | "allow-plugins": { 24 | "pestphp/pest-plugin": true 25 | } 26 | }, 27 | "minimum-stability": "stable", 28 | "prefer-stable": true, 29 | "repositories": [ 30 | { 31 | "type": "path", 32 | "url": "../" 33 | } 34 | ], 35 | "scripts": { 36 | "setup": [ 37 | "@composer install", 38 | "cp -n .env.example .env || true", 39 | "echo 'Setup complete! Please add your MISTRAL_API_KEY to the .env file.'" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Requests/Conversations/AppendToConversationRequest.php: -------------------------------------------------------------------------------- 1 | conversationId}"; 27 | } 28 | 29 | protected function defaultBody(): array 30 | { 31 | return array_filter($this->appendRequest->toArray()); 32 | } 33 | 34 | public function createDtoFromResponse(Response $response): ConversationResponse 35 | { 36 | return ConversationResponse::from($response->json()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Requests/Conversations/RestartConversationRequest.php: -------------------------------------------------------------------------------- 1 | conversationId}/restart"; 27 | } 28 | 29 | protected function defaultBody(): array 30 | { 31 | return array_filter($this->restartRequest->toArray()); 32 | } 33 | 34 | public function createDtoFromResponse(Response $response): ConversationResponse 35 | { 36 | return ConversationResponse::from($response->json()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Requests/Classifications/CreateChatClassificationRequest.php: -------------------------------------------------------------------------------- 1 | chatClassificationRequest->toArray(); 34 | } 35 | 36 | public function createDtoFromResponse(Response $response): ClassificationResponse 37 | { 38 | return ClassificationResponse::from($response->json()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Dto/Models/FTModelCard.php: -------------------------------------------------------------------------------- 1 | */ 27 | public array $aliases = [], 28 | public ?string $deprecation = null, 29 | #[MapName('deprecation_replacement_model')] 30 | public ?string $deprecationReplacementModel = null, 31 | #[MapName('default_model_temperature')] 32 | public ?float $defaultModelTemperature = null, 33 | public string $type = 'fine-tuned', 34 | public bool $archived = false, 35 | ) {} 36 | } 37 | -------------------------------------------------------------------------------- /src/Dto/Audio/TranscriptionResponse.php: -------------------------------------------------------------------------------- 1 | |null */ 15 | #[DataCollectionOf(TranscriptionWord::class)] 16 | public ?array $words = null, 17 | /** @var array|null */ 18 | #[DataCollectionOf(TranscriptionSegment::class)] 19 | public ?array $segments = null, 20 | ) {} 21 | 22 | /** 23 | * Get the full transcription text. 24 | * If the text field is null, reconstructs it from segments. 25 | */ 26 | public function getText(): ?string 27 | { 28 | if ($this->text !== null) { 29 | return $this->text; 30 | } 31 | 32 | if ($this->segments === null || count($this->segments) === 0) { 33 | return null; 34 | } 35 | 36 | return implode(' ', array_map(fn (TranscriptionSegment $segment) => $segment->text, $this->segments)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Resource/Models.php: -------------------------------------------------------------------------------- 1 | connector->send(new ListModels); 20 | } 21 | 22 | /** 23 | * List models and return typed DTO 24 | */ 25 | public function listDto(): ModelList 26 | { 27 | return $this->list()->dto(); 28 | } 29 | 30 | public function retrieve(string $modelId): BaseModelCard|FTModelCard 31 | { 32 | return $this->connector->send(new RetrieveModelRequest($modelId)) 33 | ->dto(); 34 | } 35 | 36 | public function delete(string $modelId): DeleteModelOut 37 | { 38 | return $this->connector->send(new DeleteModelRequest($modelId)) 39 | ->dto(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Dto/Chat/ChatCompletionRequest.php: -------------------------------------------------------------------------------- 1 | libraryId}/documents"; 29 | } 30 | 31 | protected function defaultBody(): array 32 | { 33 | return [ 34 | new MultipartValue( 35 | name: 'file', 36 | value: fopen($this->filePath, 'r'), 37 | filename: basename($this->filePath), 38 | ), 39 | ]; 40 | } 41 | 42 | public function createDtoFromResponse(Response $response): DocumentOut 43 | { 44 | return DocumentOut::from($response->json()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Concerns/HandlesStreamedResponses.php: -------------------------------------------------------------------------------- 1 | eof()) { 14 | $line = $this->readLine($stream); 15 | 16 | if (! str_starts_with($line, 'data:')) { 17 | continue; 18 | } 19 | 20 | $data = trim(substr($line, strlen('data:'))); 21 | 22 | if ($data === '[DONE]') { 23 | break; 24 | } 25 | 26 | $response = json_decode($data, true, flags: JSON_THROW_ON_ERROR); 27 | 28 | yield $response; 29 | } 30 | } 31 | 32 | protected function readLine($stream): string 33 | { 34 | $buffer = ''; 35 | while (! $stream->eof()) { 36 | if ('' === ($byte = $stream->read(1))) { 37 | return $buffer; 38 | } 39 | $buffer .= $byte; 40 | if ($byte === "\n") { 41 | break; 42 | } 43 | } 44 | 45 | return $buffer; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Dto/Conversations/ConversationAppendRequest.php: -------------------------------------------------------------------------------- 1 | flagged !== null) { 27 | return $this->flagged; 28 | } 29 | 30 | // Content is flagged if any category is true 31 | return $this->categories->sexual 32 | || $this->categories->hateAndDiscrimination 33 | || $this->categories->violenceAndThreats 34 | || $this->categories->dangerousAndCriminalContent 35 | || $this->categories->selfharm 36 | || $this->categories->health 37 | || $this->categories->financial 38 | || $this->categories->law 39 | || $this->categories->pii; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Requests/Files/UploadFile.php: -------------------------------------------------------------------------------- 1 | filePath), 41 | filename: basename($this->filePath) 42 | ), 43 | ]; 44 | 45 | if ($this->purpose !== null) { 46 | $body[] = new MultipartValue( 47 | name: 'purpose', 48 | value: $this->purpose->value 49 | ); 50 | } 51 | 52 | return $body; 53 | } 54 | 55 | public function createDtoFromResponse(Response $response): UploadFileOut 56 | { 57 | return UploadFileOut::from($response->json()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Dto/FineTuning/ClassifierDetailedJobOut.php: -------------------------------------------------------------------------------- 1 | env('MISTRAL_API_KEY'), 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Mistral Base URL 20 | |-------------------------------------------------------------------------- 21 | | 22 | | This URL is the base endpoint for all Mistral.ai API requests. While it's 23 | | set to Mistral's default API server, you can change it for self-hosted 24 | | models or different test environments (if applicable, in the future). 25 | | 26 | */ 27 | 28 | 'base_url' => env('MISTRAL_BASE_URL'), 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | Mistral Timeout 33 | |-------------------------------------------------------------------------- 34 | | 35 | | This configuration option defines the maximum duration (in seconds) that 36 | | your application will wait for a response when making requests to the 37 | | Mistral.ai API. By default, this value is set to 60 seconds. If you wish 38 | | to disable the timeout entirely, you can set this value to 0. 39 | | 40 | */ 41 | 42 | 'timeout' => env('MISTRAL_TIMEOUT', 60), 43 | ]; 44 | -------------------------------------------------------------------------------- /src/Enums/Model.php: -------------------------------------------------------------------------------- 1 | content, true); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Requests/FineTuning/ListJobsRequest.php: -------------------------------------------------------------------------------- 1 | $this->page, 39 | 'page_size' => $this->pageSize, 40 | 'model' => $this->model, 41 | 'created_after' => $this->createdAfter, 42 | 'created_before' => $this->createdBefore, 43 | 'created_by_me' => $this->createdByMe, 44 | 'status' => $this->status, 45 | 'wandb_project' => $this->wandbProject, 46 | 'wandb_name' => $this->wandbName, 47 | 'suffix' => $this->suffix, 48 | ], fn ($value) => $value !== null); 49 | } 50 | 51 | public function createDtoFromResponse(Response $response): JobsOut 52 | { 53 | return JobsOut::from($response->json()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Requests/Batch/ListBatchJobsRequest.php: -------------------------------------------------------------------------------- 1 | |null $status 24 | */ 25 | public function __construct( 26 | protected ?int $page = null, 27 | protected ?int $pageSize = null, 28 | protected ?string $model = null, 29 | protected ?string $agentId = null, 30 | protected ?array $metadata = null, 31 | protected ?string $createdAfter = null, 32 | protected ?bool $createdByMe = null, 33 | protected ?array $status = null, 34 | ) {} 35 | 36 | protected function defaultQuery(): array 37 | { 38 | $query = array_filter([ 39 | 'page' => $this->page, 40 | 'page_size' => $this->pageSize, 41 | 'model' => $this->model, 42 | 'agent_id' => $this->agentId, 43 | 'created_after' => $this->createdAfter, 44 | 'created_by_me' => $this->createdByMe, 45 | ], fn ($value) => $value !== null); 46 | 47 | if ($this->metadata !== null) { 48 | $query['metadata'] = json_encode($this->metadata); 49 | } 50 | 51 | if ($this->status !== null) { 52 | foreach ($this->status as $statusValue) { 53 | $query['status'][] = $statusValue; 54 | } 55 | } 56 | 57 | return $query; 58 | } 59 | 60 | public function createDtoFromResponse(Response $response): BatchJobsOut 61 | { 62 | return BatchJobsOut::from($response->json()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | # Mistral PHP Justfile 2 | # Run `just --list` to see all available commands 3 | 4 | # Default recipe to display help 5 | default: 6 | @just --list 7 | 8 | # Run all tests 9 | test: 10 | composer test 11 | 12 | # Run tests with coverage 13 | coverage: 14 | composer test-coverage 15 | 16 | # Run static analysis with PHPStan 17 | analyze: 18 | composer analyse src examples 19 | 20 | # Format code with Laravel Pint 21 | format: 22 | composer format 23 | 24 | # Run static analysis (alias for analyze) 25 | lint: analyze 26 | 27 | # Run all quality checks (format, analyze, test) 28 | check: format analyze test 29 | 30 | # Install dependencies 31 | install: 32 | composer install 33 | 34 | # Update dependencies 35 | update: 36 | composer update 37 | 38 | # Run all examples (integration tests) 39 | examples: 40 | @echo "Running all examples..." 41 | @echo "" 42 | @echo "Example 1: Getting Started" 43 | @php examples/01-getting-started/getting-started.php 44 | @echo "" 45 | @echo "Example 2: Basic Chat" 46 | @php examples/02-basic-chat/basic-chat.php 47 | @echo "" 48 | @echo "Example 3: Chat Parameters" 49 | @php examples/03-chat-parameters/chat-parameters.php 50 | @echo "" 51 | @echo "Example 4: Streaming Chat" 52 | @php examples/04-streaming-chat/streaming-chat.php 53 | @echo "" 54 | @echo "Example 5: Function Calling" 55 | @php examples/05-function-calling/function-calling.php 56 | @echo "" 57 | @echo "Example 6: Embeddings" 58 | @php examples/06-embeddings/embeddings.php 59 | @echo "" 60 | @echo "Example 7: OCR" 61 | @php examples/07-ocr/ocr.php 62 | @echo "" 63 | @echo "Example 8: Audio" 64 | @php examples/08-audio/audio.php 65 | @echo "" 66 | @echo "Example 9: Moderation" 67 | @php examples/09-moderation/moderation.php 68 | @echo "" 69 | @echo "Example 10: Error Handling" 70 | @php examples/10-error-handling/error-handling.php 71 | @echo "" 72 | @echo "✅ All examples completed successfully!" 73 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helgesverre/mistral", 3 | "description": "Laravel Client for the Mistral.ai API", 4 | "keywords": [ 5 | "laravel", 6 | "mistral", 7 | "ai", 8 | "sdk" 9 | ], 10 | "homepage": "https://github.com/helgesverre/mistral-php", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Helge Sverre", 15 | "email": "helge.sverre@gmail.com", 16 | "role": "Developer" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.2", 21 | "saloonphp/laravel-plugin": "^v3.5", 22 | "spatie/laravel-data": "^3|^4", 23 | "spatie/laravel-package-tools": "^1.16" 24 | }, 25 | "require-dev": { 26 | "laravel/pint": "^1.0", 27 | "nunomaduro/collision": "^7|^8", 28 | "larastan/larastan": "^2.0.1", 29 | "orchestra/testbench": "^8.0|^9.0", 30 | "pestphp/pest": "^2.20", 31 | "pestphp/pest-plugin-arch": "^2.0", 32 | "pestphp/pest-plugin-laravel": "^2.0", 33 | "phpstan/extension-installer": "^1.1", 34 | "phpstan/phpstan-deprecation-rules": "^1.0", 35 | "phpstan/phpstan-phpunit": "^1.0" 36 | }, 37 | "autoload": { 38 | "psr-4": { 39 | "HelgeSverre\\Mistral\\": "src/" 40 | } 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "HelgeSverre\\Mistral\\Tests\\": "tests/" 45 | } 46 | }, 47 | "scripts": { 48 | "analyse": "vendor/bin/phpstan analyse", 49 | "stan": "vendor/bin/phpstan analyse --configuration=phpstan.neon", 50 | "test": "vendor/bin/pest", 51 | "test-coverage": "vendor/bin/pest --coverage", 52 | "format": "vendor/bin/pint" 53 | }, 54 | "config": { 55 | "sort-packages": true, 56 | "allow-plugins": { 57 | "pestphp/pest-plugin": true, 58 | "phpstan/extension-installer": true 59 | } 60 | }, 61 | "extra": { 62 | "laravel": { 63 | "providers": [ 64 | "HelgeSverre\\Mistral\\MistralServiceProvider" 65 | ], 66 | "aliases": { 67 | "Mistral": "HelgeSverre\\Mistral\\Facades\\Mistral" 68 | } 69 | } 70 | }, 71 | "minimum-stability": "dev", 72 | "prefer-stable": true 73 | } 74 | -------------------------------------------------------------------------------- /src/Dto/OCR/Document.php: -------------------------------------------------------------------------------- 1 | $this->type]; 23 | 24 | // Only include the field that matches the type 25 | switch ($this->type) { 26 | case 'document_url': 27 | if ($this->documentUrl !== null) { 28 | $data['document_url'] = $this->documentUrl; 29 | } 30 | break; 31 | case 'image_url': 32 | if ($this->imageUrl !== null) { 33 | // Based on the error, image_url expects an object with a url field 34 | $data['image_url'] = [ 35 | 'url' => $this->imageUrl, 36 | ]; 37 | } 38 | break; 39 | case 'file': 40 | if ($this->fileId !== null) { 41 | $data['file_id'] = $this->fileId; 42 | } 43 | break; 44 | } 45 | 46 | return $data; 47 | } 48 | 49 | public static function fromDocumentUrl(string $url): self 50 | { 51 | return new self( 52 | type: 'document_url', 53 | documentUrl: $url, 54 | ); 55 | } 56 | 57 | public static function fromImageUrl(string $url): self 58 | { 59 | return new self( 60 | type: 'image_url', 61 | imageUrl: $url, 62 | ); 63 | } 64 | 65 | public static function fromUrl(string $url): self 66 | { 67 | // Default to document_url for backward compatibility 68 | return self::fromDocumentUrl($url); 69 | } 70 | 71 | public static function fromBase64(string $base64, string $mimeType): self 72 | { 73 | // Determine if it's an image or document based on MIME type 74 | $imageTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/avif']; 75 | 76 | if (in_array($mimeType, $imageTypes)) { 77 | return new self( 78 | type: 'image_url', 79 | imageUrl: "data:{$mimeType};base64,{$base64}", 80 | ); 81 | } 82 | 83 | return new self( 84 | type: 'document_url', 85 | documentUrl: "data:{$mimeType};base64,{$base64}", 86 | ); 87 | } 88 | 89 | public static function fromFileId(string $fileId): self 90 | { 91 | return new self( 92 | type: 'file', 93 | fileId: $fileId, 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Resource/Agents.php: -------------------------------------------------------------------------------- 1 | connector->send(new CreateAgentRequest($request)); 25 | } 26 | 27 | /** 28 | * Create a new agent and return typed DTO 29 | */ 30 | public function createDto(AgentCreationRequest $request): Agent 31 | { 32 | return $this->create($request)->dto(); 33 | } 34 | 35 | /** 36 | * List all agents with pagination 37 | */ 38 | public function list(?int $page = null, ?int $pageSize = null): Response 39 | { 40 | return $this->connector->send(new ListAgentsRequest($page, $pageSize)); 41 | } 42 | 43 | /** 44 | * List all agents with pagination and return typed DTO 45 | */ 46 | public function listDto(?int $page = null, ?int $pageSize = null): AgentList 47 | { 48 | return $this->list($page, $pageSize)->dto(); 49 | } 50 | 51 | /** 52 | * Retrieve a specific agent by ID 53 | */ 54 | public function get(string $agentId): Response 55 | { 56 | return $this->connector->send(new GetAgentRequest($agentId)); 57 | } 58 | 59 | /** 60 | * Retrieve a specific agent by ID and return typed DTO 61 | */ 62 | public function getDto(string $agentId): Agent 63 | { 64 | return $this->get($agentId)->dto(); 65 | } 66 | 67 | /** 68 | * Update an agent (creates new version) 69 | */ 70 | public function update(string $agentId, AgentUpdateRequest $request): Response 71 | { 72 | return $this->connector->send(new UpdateAgentRequest($agentId, $request)); 73 | } 74 | 75 | /** 76 | * Update an agent (creates new version) and return typed DTO 77 | */ 78 | public function updateDto(string $agentId, AgentUpdateRequest $request): Agent 79 | { 80 | return $this->update($agentId, $request)->dto(); 81 | } 82 | 83 | /** 84 | * Switch agent to a specific version 85 | */ 86 | public function updateVersion(string $agentId, int $version): Response 87 | { 88 | return $this->connector->send(new UpdateAgentVersionRequest($agentId, $version)); 89 | } 90 | 91 | /** 92 | * Switch agent to a specific version and return typed DTO 93 | */ 94 | public function updateVersionDto(string $agentId, int $version): Agent 95 | { 96 | return $this->updateVersion($agentId, $version)->dto(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Resource/Classifications.php: -------------------------------------------------------------------------------- 1 | connector->send(new CreateModerationRequest($request)); 31 | } 32 | 33 | public function moderateAsDto( 34 | string $model, 35 | string|array $input 36 | ): ModerationResponse { 37 | return $this->moderate($model, $input)->dto(); 38 | } 39 | 40 | public function moderateChat( 41 | string $model, 42 | array $input 43 | ): Response { 44 | $request = new ChatModerationRequest( 45 | model: $model, 46 | input: $input 47 | ); 48 | 49 | return $this->connector->send(new CreateChatModerationRequest($request)); 50 | } 51 | 52 | public function moderateChatAsDto( 53 | string $model, 54 | array $input 55 | ): ModerationResponse { 56 | return $this->moderateChat($model, $input)->dto(); 57 | } 58 | 59 | // Classification endpoints 60 | 61 | public function classify( 62 | string $model, 63 | string|array $input 64 | ): Response { 65 | $request = new ClassificationRequest( 66 | model: $model, 67 | input: $input 68 | ); 69 | 70 | return $this->connector->send(new CreateClassificationRequest($request)); 71 | } 72 | 73 | public function classifyAsDto( 74 | string $model, 75 | string|array $input 76 | ): ClassificationResponse { 77 | return $this->classify($model, $input)->dto(); 78 | } 79 | 80 | public function classifyChat( 81 | string $model, 82 | array $messages 83 | ): Response { 84 | $request = new ChatClassificationRequest( 85 | model: $model, 86 | messages: $messages 87 | ); 88 | 89 | return $this->connector->send(new CreateChatClassificationRequest($request)); 90 | } 91 | 92 | public function classifyChatAsDto( 93 | string $model, 94 | array $messages 95 | ): ClassificationResponse { 96 | return $this->classifyChat($model, $messages)->dto(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Resource/Fim.php: -------------------------------------------------------------------------------- 1 | connector->send(new CreateFIMCompletionRequest( 30 | new FIMCompletionRequest( 31 | model: $model, 32 | prompt: $prompt, 33 | suffix: $suffix, 34 | temperature: $temperature, 35 | topP: $topP, 36 | maxTokens: $maxTokens, 37 | minTokens: $minTokens, 38 | stream: false, 39 | randomSeed: $randomSeed, 40 | stop: $stop, 41 | ) 42 | )); 43 | } 44 | 45 | /** 46 | * Create FIM completion and return typed DTO 47 | */ 48 | public function createDto( 49 | string $model, 50 | string $prompt, 51 | ?string $suffix = null, 52 | float $temperature = 0.7, 53 | ?int $maxTokens = null, 54 | ?float $topP = null, 55 | ?int $minTokens = null, 56 | ?int $randomSeed = null, 57 | string|array|null $stop = null, 58 | ): FIMCompletionResponse { 59 | return $this->create($model, $prompt, $suffix, $temperature, $maxTokens, $topP, $minTokens, $randomSeed, $stop)->dto(); 60 | } 61 | 62 | /** 63 | * @return Generator|StreamedFIMCompletionResponse[] 64 | * 65 | * @noinspection PhpDocSignatureInspection 66 | * @noinspection PhpDocMissingThrowsInspection 67 | */ 68 | public function createStreamed( 69 | string $model, 70 | string $prompt, 71 | ?string $suffix = null, 72 | float $temperature = 0.7, 73 | ?int $maxTokens = null, 74 | ?float $topP = null, 75 | ?int $minTokens = null, 76 | ?int $randomSeed = null, 77 | string|array|null $stop = null, 78 | ): Generator { 79 | $response = $this->connector->send(new CreateFIMCompletionRequest( 80 | new FIMCompletionRequest( 81 | model: $model, 82 | prompt: $prompt, 83 | suffix: $suffix, 84 | temperature: $temperature, 85 | topP: $topP, 86 | maxTokens: $maxTokens, 87 | minTokens: $minTokens, 88 | stream: true, 89 | randomSeed: $randomSeed, 90 | stop: $stop, 91 | ) 92 | )); 93 | 94 | foreach ($this->getStreamIterator($response->stream()) as $fimResponse) { 95 | yield StreamedFIMCompletionResponse::from($fimResponse); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- 1 | # CLAUDE.md 2 | 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 | 5 | ## Commands 6 | 7 | ### Build/Test/Lint 8 | 9 | ```bash 10 | # Run tests 11 | composer test 12 | 13 | # Run tests with coverage 14 | composer test-coverage 15 | 16 | # Run static analysis (PHPStan level 5) 17 | composer analyse src 18 | 19 | # Format code with Laravel Pint 20 | composer format 21 | ``` 22 | 23 | ### Testing a specific test file 24 | 25 | ```bash 26 | composer test -- tests/Resources/YourResourceTest.php 27 | ``` 28 | 29 | ### Requirements 30 | 31 | - PHP 8.2+ 32 | - Laravel 8.x, 9.x, or 11.x compatibility (tested via Orchestra Testbench) 33 | 34 | ## High-level Architecture 35 | 36 | This is a Laravel package for the Mistral.ai API built on top of Saloon PHP HTTP client. 37 | 38 | ### Key Patterns 39 | 40 | 1. **Resource Pattern**: Each API endpoint group has a Resource class (e.g., `Chat`, `OCR`, `Embedding`) that extends `Saloon\Http\BaseResource` and provides high-level methods. 41 | 42 | 2. **Request/Response DTOs**: 43 | - Request DTOs in `src/Dto/[Feature]/` extend `Spatie\LaravelData\Data` 44 | - Use `#[MapName()]` attributes for snake_case to camelCase mapping 45 | - Response DTOs match API response structure exactly 46 | 47 | 3. **Request Classes**: In `src/Requests/[Feature]/` extend `Saloon\Http\Request` with: 48 | - `HasBody` interface and `HasJsonBody` trait 49 | - Constructor accepts DTO request object 50 | - `defaultBody()` returns `array_filter($dto->toArray())` to remove null values 51 | - `createDtoFromResponse()` converts responses to DTOs 52 | 53 | 4. **Main Connector**: `src/Mistral.php` extends `Saloon\Http\Connector` and provides: 54 | - Fluent access to resources via methods like `->chat()`, `->ocr()`, etc. 55 | - Token authentication via `TokenAuthenticator` 56 | - Configurable timeout and base URL 57 | 58 | ### Adding New Endpoints 59 | 60 | 1. Create DTOs in `src/Dto/[Feature]/` 61 | 2. Create Request class in `src/Requests/[Feature]/` 62 | 3. Create Resource class in `src/Resource/` 63 | 4. Add resource method to `Mistral.php` 64 | 5. Create tests in `tests/Resources/` 65 | 6. Add fixture in `tests/Fixtures/Saloon/` 66 | 67 | ### Testing Pattern 68 | 69 | Tests use Pest PHP with the following patterns: 70 | 71 | 1. **Mocking**: Use `Saloon::fake()` to mock HTTP requests 72 | 2. **Fixtures**: JSON response fixtures in `tests/Fixtures/Saloon/` follow naming: `{feature}.{method}[-optional-descriptor].json` 73 | 3. **Assertions**: Use `Saloon::assertSent()` to verify requests were made 74 | 4. **DTOs**: Call `$response->dto()` to test DTO conversion 75 | 76 | ### Streaming Responses 77 | 78 | Resources that support streaming (Chat, SimpleChat, FIM, Audio, Conversations): 79 | 80 | - Include `use HandlesStreamedResponses` trait 81 | - Return `Generator` that yields DTO instances 82 | - Use `getStreamIterator($response->stream())` to parse SSE format 83 | 84 | ## Important Notes 85 | 86 | - All API parameters use camelCase in PHP but snake_case in JSON (handled by `#[MapName()]` attribute) 87 | - Request DTOs use nullable properties with defaults for maximum flexibility 88 | - Response DTOs use Spatie Laravel Data collections (`DataCollection`) for arrays 89 | - The `array_filter()` in Request `defaultBody()` removes null values before sending to API 90 | - PHPStan is configured at level 5 - maintain this strict typing standard 91 | -------------------------------------------------------------------------------- /src/Mistral.php: -------------------------------------------------------------------------------- 1 | timeout; 46 | } 47 | 48 | public function getRequestTimeout(): float 49 | { 50 | return $this->timeout; 51 | } 52 | 53 | protected function defaultAuth(): TokenAuthenticator 54 | { 55 | return new TokenAuthenticator($this->apiKey); 56 | } 57 | 58 | public function resolveBaseUrl(): string 59 | { 60 | return $this->baseUrl ?: 'https://api.mistral.ai/v1'; 61 | } 62 | 63 | public function chat(): Chat 64 | { 65 | return new Chat($this); 66 | } 67 | 68 | public function simpleChat(): SimpleChat 69 | { 70 | return new SimpleChat($this); 71 | } 72 | 73 | public function embedding(): Embedding 74 | { 75 | return new Embedding($this); 76 | } 77 | 78 | public function models(): Models 79 | { 80 | return new Models($this); 81 | } 82 | 83 | public function ocr(): OCR 84 | { 85 | return new OCR($this); 86 | } 87 | 88 | public function files(): Files 89 | { 90 | return new Files($this); 91 | } 92 | 93 | public function fineTuning(): FineTuning 94 | { 95 | return new FineTuning($this); 96 | } 97 | 98 | public function fim(): Fim 99 | { 100 | return new Fim($this); 101 | } 102 | 103 | public function audio(): Audio 104 | { 105 | return new Audio($this); 106 | } 107 | 108 | public function batch(): Batch 109 | { 110 | return new Batch($this); 111 | } 112 | 113 | public function classifications(): Classifications 114 | { 115 | return new Classifications($this); 116 | } 117 | 118 | public function conversations(): Conversations 119 | { 120 | return new Conversations($this); 121 | } 122 | 123 | public function agents(): Agents 124 | { 125 | return new Agents($this); 126 | } 127 | 128 | public function libraries(): Libraries 129 | { 130 | return new Libraries($this); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/Requests/Audio/CreateTranscriptionStreamRequest.php: -------------------------------------------------------------------------------- 1 | |null $timestampGranularities Timestamp granularities to include 32 | */ 33 | public function __construct( 34 | protected readonly string $filePath, 35 | protected readonly string $model, 36 | protected readonly ?string $language = null, 37 | protected readonly ?string $prompt = null, 38 | protected readonly ?ResponseFormat $responseFormat = null, 39 | protected readonly ?float $temperature = null, 40 | protected readonly ?array $timestampGranularities = null, 41 | ) {} 42 | 43 | public function resolveEndpoint(): string 44 | { 45 | return '/audio/transcriptions#stream'; 46 | } 47 | 48 | protected function defaultBody(): array 49 | { 50 | $body = [ 51 | new MultipartValue( 52 | name: 'file', 53 | value: file_get_contents($this->filePath), 54 | filename: basename($this->filePath) 55 | ), 56 | new MultipartValue( 57 | name: 'model', 58 | value: $this->model 59 | ), 60 | ]; 61 | 62 | if ($this->language !== null) { 63 | $body[] = new MultipartValue( 64 | name: 'language', 65 | value: $this->language 66 | ); 67 | } 68 | 69 | if ($this->prompt !== null) { 70 | $body[] = new MultipartValue( 71 | name: 'prompt', 72 | value: $this->prompt 73 | ); 74 | } 75 | 76 | if ($this->responseFormat !== null) { 77 | $body[] = new MultipartValue( 78 | name: 'response_format', 79 | value: $this->responseFormat->value 80 | ); 81 | } 82 | 83 | if ($this->temperature !== null) { 84 | $body[] = new MultipartValue( 85 | name: 'temperature', 86 | value: (string) $this->temperature 87 | ); 88 | } 89 | 90 | if ($this->timestampGranularities !== null && count($this->timestampGranularities) > 0) { 91 | $granularities = array_map(fn (TimestampGranularity $g) => $g->value, $this->timestampGranularities); 92 | $body[] = new MultipartValue( 93 | name: 'timestamp_granularities[]', 94 | value: json_encode($granularities) 95 | ); 96 | } 97 | 98 | return $body; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Requests/Audio/CreateTranscriptionRequest.php: -------------------------------------------------------------------------------- 1 | |null $timestampGranularities Timestamp granularities to include 34 | */ 35 | public function __construct( 36 | protected readonly string $filePath, 37 | protected readonly string $model, 38 | protected readonly ?string $language = null, 39 | protected readonly ?string $prompt = null, 40 | protected readonly ?ResponseFormat $responseFormat = null, 41 | protected readonly ?float $temperature = null, 42 | protected readonly ?array $timestampGranularities = null, 43 | ) {} 44 | 45 | public function resolveEndpoint(): string 46 | { 47 | return '/audio/transcriptions'; 48 | } 49 | 50 | protected function defaultBody(): array 51 | { 52 | $body = [ 53 | new MultipartValue( 54 | name: 'file', 55 | value: file_get_contents($this->filePath), 56 | filename: basename($this->filePath) 57 | ), 58 | new MultipartValue( 59 | name: 'model', 60 | value: $this->model 61 | ), 62 | ]; 63 | 64 | if ($this->language !== null) { 65 | $body[] = new MultipartValue( 66 | name: 'language', 67 | value: $this->language 68 | ); 69 | } 70 | 71 | if ($this->prompt !== null) { 72 | $body[] = new MultipartValue( 73 | name: 'prompt', 74 | value: $this->prompt 75 | ); 76 | } 77 | 78 | if ($this->responseFormat !== null) { 79 | $body[] = new MultipartValue( 80 | name: 'response_format', 81 | value: $this->responseFormat->value 82 | ); 83 | } 84 | 85 | if ($this->temperature !== null) { 86 | $body[] = new MultipartValue( 87 | name: 'temperature', 88 | value: (string) $this->temperature 89 | ); 90 | } 91 | 92 | if ($this->timestampGranularities !== null && count($this->timestampGranularities) > 0) { 93 | $granularities = array_map(fn (TimestampGranularity $g) => $g->value, $this->timestampGranularities); 94 | $body[] = new MultipartValue( 95 | name: 'timestamp_granularities[]', 96 | value: json_encode($granularities) 97 | ); 98 | } 99 | 100 | return $body; 101 | } 102 | 103 | public function createDtoFromResponse(Response $response): TranscriptionResponse 104 | { 105 | return TranscriptionResponse::from($response->json()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Resource/Batch.php: -------------------------------------------------------------------------------- 1 | |null $status Array of BatchJobStatus values 21 | */ 22 | public function list( 23 | ?int $page = null, 24 | ?int $pageSize = null, 25 | ?string $model = null, 26 | ?string $agentId = null, 27 | ?array $metadata = null, 28 | ?string $createdAfter = null, 29 | ?bool $createdByMe = null, 30 | ?array $status = null, 31 | ): Response { 32 | return $this->connector->send(new ListBatchJobsRequest( 33 | page: $page, 34 | pageSize: $pageSize, 35 | model: $model, 36 | agentId: $agentId, 37 | metadata: $metadata, 38 | createdAfter: $createdAfter, 39 | createdByMe: $createdByMe, 40 | status: $status, 41 | )); 42 | } 43 | 44 | /** 45 | * Create a new batch job 46 | */ 47 | public function create(BatchJobIn $batchJobIn): Response 48 | { 49 | return $this->connector->send(new CreateBatchJobRequest($batchJobIn)); 50 | } 51 | 52 | /** 53 | * Get detailed information about a batch job 54 | * 55 | * @param string $jobId The UUID of the batch job 56 | */ 57 | public function get(string $jobId): Response 58 | { 59 | return $this->connector->send(new GetBatchJobRequest($jobId)); 60 | } 61 | 62 | /** 63 | * Cancel a running batch job 64 | * 65 | * @param string $jobId The UUID of the batch job 66 | */ 67 | public function cancel(string $jobId): Response 68 | { 69 | return $this->connector->send(new CancelBatchJobRequest($jobId)); 70 | } 71 | 72 | /** 73 | * Helper method to convert list response to DTO 74 | * 75 | * @param array|null $status Array of BatchJobStatus values 76 | */ 77 | public function listAsDto( 78 | ?int $page = null, 79 | ?int $pageSize = null, 80 | ?string $model = null, 81 | ?string $agentId = null, 82 | ?array $metadata = null, 83 | ?string $createdAfter = null, 84 | ?bool $createdByMe = null, 85 | ?array $status = null, 86 | ): BatchJobsOut { 87 | $response = $this->list( 88 | page: $page, 89 | pageSize: $pageSize, 90 | model: $model, 91 | agentId: $agentId, 92 | metadata: $metadata, 93 | createdAfter: $createdAfter, 94 | createdByMe: $createdByMe, 95 | status: $status, 96 | ); 97 | 98 | return BatchJobsOut::from($response->json()); 99 | } 100 | 101 | /** 102 | * Helper method to convert create response to DTO 103 | */ 104 | public function createAsDto(BatchJobIn $batchJobIn): BatchJobOut 105 | { 106 | $response = $this->create($batchJobIn); 107 | 108 | return BatchJobOut::from($response->json()); 109 | } 110 | 111 | /** 112 | * Helper method to convert get response to DTO 113 | */ 114 | public function getAsDto(string $jobId): BatchJobOut 115 | { 116 | $response = $this->get($jobId); 117 | 118 | return BatchJobOut::from($response->json()); 119 | } 120 | 121 | /** 122 | * Helper method to convert cancel response to DTO 123 | */ 124 | public function cancelAsDto(string $jobId): BatchJobOut 125 | { 126 | $response = $this->cancel($jobId); 127 | 128 | return BatchJobOut::from($response->json()); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/Resource/Audio.php: -------------------------------------------------------------------------------- 1 | |null $timestampGranularities Timestamp detail levels 29 | */ 30 | public function transcribe( 31 | string $filePath, 32 | string $model, 33 | ?string $language = null, 34 | ?string $prompt = null, 35 | ?ResponseFormat $responseFormat = null, 36 | ?float $temperature = null, 37 | ?array $timestampGranularities = null, 38 | ): Response { 39 | return $this->connector->send( 40 | new CreateTranscriptionRequest( 41 | filePath: $filePath, 42 | model: $model, 43 | language: $language, 44 | prompt: $prompt, 45 | responseFormat: $responseFormat, 46 | temperature: $temperature, 47 | timestampGranularities: $timestampGranularities, 48 | ) 49 | ); 50 | } 51 | 52 | /** 53 | * Transcribe an audio file to text and return typed DTO 54 | */ 55 | public function transcribeDto( 56 | string $filePath, 57 | string $model, 58 | ?string $language = null, 59 | ?string $prompt = null, 60 | ?ResponseFormat $responseFormat = null, 61 | ?float $temperature = null, 62 | ?array $timestampGranularities = null, 63 | ): TranscriptionResponse { 64 | return $this->transcribe($filePath, $model, $language, $prompt, $responseFormat, $temperature, $timestampGranularities)->dto(); 65 | } 66 | 67 | /** 68 | * Transcribe an audio file with streaming response 69 | * 70 | * @param string $filePath Path to the audio file 71 | * @param string $model Model to use (e.g., "whisper-large-v3") 72 | * @param string|null $language Language code (ISO-639-1) 73 | * @param string|null $prompt Optional prompt to guide transcription 74 | * @param ResponseFormat|null $responseFormat Output format (json, text, verbose_json) 75 | * @param float|null $temperature Sampling temperature (0-1) 76 | * @param array|null $timestampGranularities Timestamp detail levels 77 | * @return Generator> 78 | */ 79 | public function transcribeStreamed( 80 | string $filePath, 81 | string $model, 82 | ?string $language = null, 83 | ?string $prompt = null, 84 | ?ResponseFormat $responseFormat = null, 85 | ?float $temperature = null, 86 | ?array $timestampGranularities = null, 87 | ): Generator { 88 | $response = $this->connector->send( 89 | new CreateTranscriptionStreamRequest( 90 | filePath: $filePath, 91 | model: $model, 92 | language: $language, 93 | prompt: $prompt, 94 | responseFormat: $responseFormat, 95 | temperature: $temperature, 96 | timestampGranularities: $timestampGranularities, 97 | ) 98 | ); 99 | 100 | yield from $this->getStreamIterator($response->stream()); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Resource/SimpleChat.php: -------------------------------------------------------------------------------- 1 | value, 22 | float $temperature = 0.7, 23 | int $maxTokens = 2000, 24 | int $topP = 1, 25 | bool $safeMode = false, 26 | bool $jsonMode = false, 27 | ?int $randomSeed = null 28 | ): SimpleChatResponse { 29 | if ($jsonMode) { 30 | $this->validateJsonModeCompatible($model); 31 | } 32 | 33 | $response = $this->connector->send(new CreateChatCompletion( 34 | new ChatCompletionRequest( 35 | model: $model, 36 | messages: $messages, 37 | temperature: $temperature, 38 | topP: $topP, 39 | maxTokens: $maxTokens, 40 | stream: false, 41 | safeMode: $safeMode, 42 | randomSeed: $randomSeed, 43 | responseFormat: $jsonMode ? ['type' => 'json_object'] : null, 44 | ) 45 | )); 46 | 47 | return SimpleChatResponse::from([ 48 | 'id' => $response->json('id'), 49 | 'object' => $response->json('object'), 50 | 'created' => $response->json('created'), 51 | 'role' => $response->json('choices.0.message.role'), 52 | 'content' => $response->json('choices.0.message.content'), 53 | 'finishReason' => $response->json('choices.0.finish_reason'), 54 | 'model' => $response->json('model'), 55 | 'promptTokens' => $response->json('usage.prompt_tokens'), 56 | 'completionTokens' => $response->json('usage.completion_tokens'), 57 | 'totalTokens' => $response->json('usage.total_tokens'), 58 | ]); 59 | } 60 | 61 | /** 62 | * @return Generator|SimpleStreamChunk[] 63 | * 64 | * @noinspection PhpDocSignatureInspection 65 | * @noinspection PhpDocMissingThrowsInspection 66 | */ 67 | public function stream( 68 | array $messages, 69 | string $model = Model::mistral7b->value, 70 | float $temperature = 0.7, 71 | int $maxTokens = 2000, 72 | int $topP = 1, 73 | bool $safeMode = false, 74 | bool $jsonMode = false, 75 | ?int $randomSeed = null, 76 | ): Generator { 77 | if ($jsonMode) { 78 | $this->validateJsonModeCompatible($model); 79 | } 80 | 81 | $response = $this->connector->send(new CreateChatCompletion( 82 | new ChatCompletionRequest( 83 | model: $model, 84 | messages: $messages, 85 | temperature: $temperature, 86 | topP: $topP, 87 | maxTokens: $maxTokens, 88 | stream: true, 89 | safeMode: $safeMode, 90 | randomSeed: $randomSeed, 91 | responseFormat: $jsonMode ? ['type' => 'json_object'] : null, 92 | ) 93 | )); 94 | 95 | foreach ($this->getStreamIterator($response->stream()) as $chatResponse) { 96 | yield SimpleStreamChunk::from([ 97 | 'id' => $chatResponse['id'] ?? null, 98 | 'model' => $chatResponse['model'] ?? null, 99 | 'object' => $chatResponse['object'] ?? null, 100 | 'created' => $chatResponse['created'] ?? null, 101 | 'role' => $chatResponse['choices'][0]['delta']['role'] ?? null, 102 | 'content' => $chatResponse['choices'][0]['delta']['content'] ?? null, 103 | 'finishReason' => $chatResponse['choices'][0]['finish_reason'] ?? null, 104 | ]); 105 | } 106 | } 107 | 108 | protected function validateJsonModeCompatible(string $model): void 109 | { 110 | $jsonCompatible = Model::withJsonModeSupport(); 111 | if (in_array($model, $jsonCompatible) === false) { 112 | throw new InvalidArgumentException('Only '.implode(', ', $jsonCompatible).' models support JSON mode'); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Resource/Libraries.php: -------------------------------------------------------------------------------- 1 | connector->send(new ListLibraries($page, $pageSize)); 36 | } 37 | 38 | /** 39 | * Create a new library 40 | */ 41 | public function create(LibraryIn $library): Response 42 | { 43 | return $this->connector->send(new CreateLibrary($library)); 44 | } 45 | 46 | /** 47 | * Get library details 48 | */ 49 | public function get(string $libraryId): Response 50 | { 51 | return $this->connector->send(new GetLibrary($libraryId)); 52 | } 53 | 54 | /** 55 | * Update library metadata 56 | */ 57 | public function update(string $libraryId, LibraryInUpdate $library): Response 58 | { 59 | return $this->connector->send(new UpdateLibrary($libraryId, $library)); 60 | } 61 | 62 | /** 63 | * Delete a library 64 | */ 65 | public function delete(string $libraryId): Response 66 | { 67 | return $this->connector->send(new DeleteLibrary($libraryId)); 68 | } 69 | 70 | /** 71 | * List documents in a library 72 | */ 73 | public function listDocuments( 74 | string $libraryId, 75 | ?string $search = null, 76 | ?int $page = null, 77 | ?int $pageSize = null 78 | ): Response { 79 | return $this->connector->send(new ListDocuments($libraryId, $search, $page, $pageSize)); 80 | } 81 | 82 | /** 83 | * Upload a document to a library 84 | */ 85 | public function uploadDocument(string $libraryId, string $filePath): Response 86 | { 87 | return $this->connector->send(new UploadDocument($libraryId, $filePath)); 88 | } 89 | 90 | /** 91 | * Get document details 92 | */ 93 | public function getDocument(string $libraryId, string $documentId): Response 94 | { 95 | return $this->connector->send(new GetDocument($libraryId, $documentId)); 96 | } 97 | 98 | /** 99 | * Update document metadata 100 | */ 101 | public function updateDocument( 102 | string $libraryId, 103 | string $documentId, 104 | DocumentUpdateIn $update 105 | ): Response { 106 | return $this->connector->send(new UpdateDocument($libraryId, $documentId, $update)); 107 | } 108 | 109 | /** 110 | * Delete a document from a library 111 | */ 112 | public function deleteDocument(string $libraryId, string $documentId): Response 113 | { 114 | return $this->connector->send(new DeleteDocument($libraryId, $documentId)); 115 | } 116 | 117 | /** 118 | * List sharing/access control for a library 119 | */ 120 | public function listSharing(string $libraryId): Response 121 | { 122 | return $this->connector->send(new ListSharing($libraryId)); 123 | } 124 | 125 | /** 126 | * Create or update library sharing/access 127 | */ 128 | public function createSharing(string $libraryId, SharingIn $sharing): Response 129 | { 130 | return $this->connector->send(new CreateSharing($libraryId, $sharing)); 131 | } 132 | 133 | /** 134 | * Delete library sharing/access 135 | */ 136 | public function deleteSharing(string $libraryId, SharingDelete $sharing): Response 137 | { 138 | return $this->connector->send(new DeleteSharing($libraryId, $sharing)); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Resource/OCR.php: -------------------------------------------------------------------------------- 1 | connector->send(new ProcessDocument( 46 | new OCRRequest( 47 | model: $model, 48 | document: $documentDto, 49 | includeImageBase64: $includeImageBase64, 50 | ) 51 | )); 52 | } 53 | 54 | /** 55 | * Process a document with OCR and return typed DTO 56 | */ 57 | public function processDto( 58 | string $model = 'mistral-ocr-latest', 59 | Document|string|null $document = null, 60 | ?string $mimeType = null, 61 | ?bool $includeImageBase64 = null, 62 | ): OCRResponse { 63 | return $this->process($model, $document, $mimeType, $includeImageBase64)->dto(); 64 | } 65 | 66 | /** 67 | * Process a document from URL 68 | * 69 | * @param string $url The URL of the document to process 70 | * @param string $model The OCR model to use 71 | * @param bool|null $includeImageBase64 Whether to include base64 encoded images in the response 72 | */ 73 | public function processUrl( 74 | string $url, 75 | string $model = 'mistral-ocr-latest', 76 | ?bool $includeImageBase64 = null, 77 | ): Response { 78 | return $this->process( 79 | model: $model, 80 | document: Document::fromUrl($url), 81 | includeImageBase64: $includeImageBase64, 82 | ); 83 | } 84 | 85 | /** 86 | * Process a document from URL and return typed DTO 87 | */ 88 | public function processUrlDto( 89 | string $url, 90 | string $model = 'mistral-ocr-latest', 91 | ?bool $includeImageBase64 = null, 92 | ): OCRResponse { 93 | return $this->processUrl($url, $model, $includeImageBase64)->dto(); 94 | } 95 | 96 | /** 97 | * Process a document from base64 encoded data 98 | * 99 | * @param string $base64 The base64 encoded document 100 | * @param string $mimeType The MIME type of the document 101 | * @param string $model The OCR model to use 102 | * @param bool|null $includeImageBase64 Whether to include base64 encoded images in the response 103 | */ 104 | public function processBase64( 105 | string $base64, 106 | string $mimeType, 107 | string $model = 'mistral-ocr-latest', 108 | ?bool $includeImageBase64 = null, 109 | ): Response { 110 | return $this->process( 111 | model: $model, 112 | document: Document::fromBase64($base64, $mimeType), 113 | includeImageBase64: $includeImageBase64, 114 | ); 115 | } 116 | 117 | /** 118 | * Process a document from base64 encoded data and return typed DTO 119 | */ 120 | public function processBase64Dto( 121 | string $base64, 122 | string $mimeType, 123 | string $model = 'mistral-ocr-latest', 124 | ?bool $includeImageBase64 = null, 125 | ): OCRResponse { 126 | return $this->processBase64($base64, $mimeType, $model, $includeImageBase64)->dto(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/Resource/Chat.php: -------------------------------------------------------------------------------- 1 | value, 22 | float $temperature = 0.7, 23 | int $maxTokens = 2000, 24 | float $topP = 1.0, 25 | bool $safeMode = false, 26 | bool $stream = false, 27 | ?int $randomSeed = null, 28 | ?array $tools = null, 29 | ?string $toolChoice = null, 30 | ?array $responseFormat = null, 31 | string|array|null $stop = null, 32 | ?float $presencePenalty = null, 33 | ?float $frequencyPenalty = null, 34 | ?array $prediction = null, 35 | ): Response { 36 | return $this->connector->send(new CreateChatCompletion( 37 | new ChatCompletionRequest( 38 | model: $model, 39 | messages: $messages, 40 | temperature: $temperature, 41 | topP: $topP, 42 | maxTokens: $maxTokens, 43 | stream: $stream, 44 | safeMode: $safeMode, 45 | randomSeed: $randomSeed, 46 | tools: $tools, 47 | toolChoice: $toolChoice, 48 | responseFormat: $responseFormat, 49 | stop: $stop, 50 | presencePenalty: $presencePenalty, 51 | frequencyPenalty: $frequencyPenalty, 52 | prediction: $prediction, 53 | ) 54 | )); 55 | } 56 | 57 | /** 58 | * Create chat completion and return typed DTO 59 | */ 60 | public function createDto( 61 | array $messages, 62 | string $model = Model::mistral7b->value, 63 | float $temperature = 0.7, 64 | int $maxTokens = 2000, 65 | float $topP = 1.0, 66 | bool $safeMode = false, 67 | ?int $randomSeed = null, 68 | ?array $tools = null, 69 | ?string $toolChoice = null, 70 | ?array $responseFormat = null, 71 | string|array|null $stop = null, 72 | ?float $presencePenalty = null, 73 | ?float $frequencyPenalty = null, 74 | ?array $prediction = null, 75 | ): ChatCompletionResponse { 76 | return $this->create( 77 | messages: $messages, 78 | model: $model, 79 | temperature: $temperature, 80 | maxTokens: $maxTokens, 81 | topP: $topP, 82 | safeMode: $safeMode, 83 | stream: false, // DTO method always non-streaming 84 | randomSeed: $randomSeed, 85 | tools: $tools, 86 | toolChoice: $toolChoice, 87 | responseFormat: $responseFormat, 88 | stop: $stop, 89 | presencePenalty: $presencePenalty, 90 | frequencyPenalty: $frequencyPenalty, 91 | prediction: $prediction, 92 | )->dto(); 93 | } 94 | 95 | /** 96 | * @return Generator|StreamedChatCompletionResponse[] 97 | * 98 | * @noinspection PhpDocSignatureInspection 99 | * @noinspection PhpDocMissingThrowsInspection 100 | */ 101 | public function createStreamed( 102 | array $messages, 103 | string $model = Model::small->value, 104 | float $temperature = 0.7, 105 | int $maxTokens = 2000, 106 | int $topP = 1, 107 | bool $safeMode = false, 108 | ?int $randomSeed = null, 109 | ?array $responseFormat = null, 110 | string|array|null $stop = null, 111 | ?float $presencePenalty = null, 112 | ?float $frequencyPenalty = null, 113 | ?array $prediction = null, 114 | ): Generator { 115 | $response = $this->connector->send(new CreateChatCompletion( 116 | new ChatCompletionRequest( 117 | model: $model, 118 | messages: $messages, 119 | temperature: $temperature, 120 | topP: $topP, 121 | maxTokens: $maxTokens, 122 | stream: true, 123 | safeMode: $safeMode, 124 | randomSeed: $randomSeed, 125 | responseFormat: $responseFormat, 126 | stop: $stop, 127 | presencePenalty: $presencePenalty, 128 | frequencyPenalty: $frequencyPenalty, 129 | prediction: $prediction, 130 | ) 131 | )); 132 | 133 | foreach ($this->getStreamIterator($response->stream()) as $chatResponse) { 134 | yield StreamedChatCompletionResponse::from($chatResponse); 135 | } 136 | } 137 | } 138 | --------------------------------------------------------------------------------