Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4224134
feat: implement Gemini File Search APIs (Stores & Documents)
jfradj Nov 30, 2025
0156009
feat: Add Google Maps and File Search tool support
jfradj Dec 1, 2025
9c8a1fa
docs: Update README.md with File Search APIs documentation
jfradj Dec 1, 2025
6bc4928
Merge branch 'feature/add-new-tools' into feature/new-gemini-apis
jfradj Dec 1, 2025
24e6f94
Update src/Resources/FileSearchStores.php
jfradj Dec 15, 2025
f44877c
Update src/Responses/FileSearchStores/UploadResponse.php
jfradj Dec 15, 2025
eb0fb54
Update README.md
jfradj Dec 15, 2025
6429503
Update src/Requests/FileSearchStores/UploadRequest.php
jfradj Dec 15, 2025
4645154
Update src/Requests/FileSearchStores/UploadRequest.php
jfradj Dec 15, 2025
da3d1ea
Update README.md
jfradj Dec 15, 2025
ce5456e
Update README.md
jfradj Dec 15, 2025
a4e837c
Update README.md
jfradj Dec 15, 2025
b049f0b
Update src/Contracts/Resources/FileSearchStoresContract.php
jfradj Dec 15, 2025
8979ef2
Update src/Requests/FileSearchStores/DeleteRequest.php
jfradj Dec 15, 2025
f8d2ae9
Update README.md
jfradj Dec 15, 2025
bdb0951
Update README.md
jfradj Dec 15, 2025
29e3ff4
Fix README: replace non-existent fileSearchDocuments with fileSearchS…
jfradj Dec 15, 2025
195da56
fix: README
jfradj Dec 15, 2025
4545a35
Merge branch 'feature/add-file-search-api' into feature/add-new-tools
jfradj Dec 15, 2025
19aecf7
Update README.md
jfradj Dec 15, 2025
3f9748f
feat: add Maps + FileSearch grounding metadata
jfradj Dec 24, 2025
fb1f20b
Merge branch 'feature/add-new-tools' into feature/new-gemini-apis
jfradj Dec 24, 2025
395d15e
chore: lint
jfradj Dec 24, 2025
752a209
fix: Gemini API response doesn't follow its own documentation (https:…
jfradj Dec 26, 2025
743b881
fix: Do not set a default display name
jfradj Dec 26, 2025
cb8b9f1
fix: Do not throw exception on unmapped mimetype + try to infer mimet…
jfradj Dec 26, 2025
81c2fc4
fix: request was malformed if no metadata was provided
jfradj Dec 26, 2025
7e2accc
feat: Add custom metadata support to fileSearchStores upload
jfradj Dec 26, 2025
a2f2452
feat: Add state, sizeBytes, and mimeType to DocumentResponse
jfradj Dec 26, 2025
1d35001
fix: phpstan errors in FileSearchStores
jfradj Dec 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 186 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
- [Function calling](#function-calling)
- [Code Execution](#code-execution)
- [Grounding with Google Search](#grounding-with-google-search)
- [Grounding with Google Maps](#grounding-with-google-maps)
- [Grounding with File Search](#grounding-with-file-search)
- [System Instructions](#system-instructions)
- [Speech generation](#speech-generation)
- [Thinking Mode](#thinking-mode)
Expand All @@ -49,6 +51,17 @@
- [Update Cached Content](#update-cached-content)
- [Delete Cached Content](#delete-cached-content)
- [Use Cached Content](#use-cached-content)
- [File Search Stores](#file-search-stores)
- [Create File Search Store](#create-file-search-store)
- [Get File Search Store](#get-file-search-store)
- [List File Search Stores](#list-file-search-stores)
- [Delete File Search Store](#delete-file-search-store)
- [Update File Search Store](#update-file-search-store)
- [File Search Documents](#file-search-documents)
- [Create File Search Document](#create-file-search-document)
- [Get File Search Document](#get-file-search-document)
- [List File Search Documents](#list-file-search-documents)
- [Delete File Search Document](#delete-file-search-document)
- [Embedding Resource](#embedding-resource)
- [Models](#models)
- [List Models](#list-models)
Expand Down Expand Up @@ -482,6 +495,59 @@ if ($groundingMetadata !== null) {
}
```

#### Grounding with Google Maps
Grounding with Google Maps allows the model to utilize real-world geographical data. This enables more precise location-based responses, such as finding nearby points of interest.

```php
use Gemini\Data\GoogleMaps;
use Gemini\Data\RetrievalConfig;
use Gemini\Data\Tool;
use Gemini\Data\ToolConfig;

$tool = new Tool(
googleMaps: new GoogleMaps(enableWidget: true)
);

$toolConfig = new ToolConfig(
retrievalConfig: new RetrievalConfig(
latitude: 40.758896,
longitude: -73.985130
)
);

$response = $client
->generativeModel(model: 'gemini-2.0-flash')
->withTool($tool)
->withToolConfig($toolConfig)
->generateContent('Find coffee shops near me');

echo $response->text();
// (Model output referencing coffee shops)
```

#### Grounding with File Search
Grounding with File Search enables the model to retrieve and utilize information from your indexed files. This is useful for answering questions based on private or extensive document collections.

```php
use Gemini\Data\FileSearch;
use Gemini\Data\Tool;

$tool = new Tool(
fileSearch: new FileSearch(
fileSearchStoreNames: ['files/my-document-store'],
metadataFilter: 'author = "Robert Graves"'
)
);

$response = $client
->generativeModel(model: 'gemini-2.0-flash')
->withTool($tool)
->generateContent('Summarize the document about Greek myths by Robert Graves');

echo $response->text();
// (Model output summarizing the document)
```

#### System Instructions
System instructions let you steer the behavior of the model based on your specific needs and use cases. You can set the role and personality of the model, define the format of responses, and provide goals and guardrails for model behavior.

Expand Down Expand Up @@ -631,6 +697,7 @@ Every prompt you send to the model includes parameter values that control how th

Also, you can use safety settings to adjust the likelihood of getting responses that may be considered harmful. By default, safety settings block content with medium and/or high probability of being unsafe content across all dimensions. Learn more about [safety settings](https://ai.google.dev/docs/concepts#safety_setting).

When using tools like `GoogleMaps`, you may also provide additional configuration via `ToolConfig`, such as `RetrievalConfig` for geographical context.

```php
use Gemini\Data\GenerationConfig;
Expand Down Expand Up @@ -834,6 +901,125 @@ echo "Cached tokens used: {$response->usageMetadata->cachedContentTokenCount}\n"
echo "New tokens used: {$response->usageMetadata->promptTokenCount}\n";
```

### File Search Stores

File search allows you to search files that were uploaded through the File API.

#### Create File Search Store
Create a file search store.

```php
use Gemini\Enums\FileState;
use Gemini\Enums\MimeType;
use Gemini\Enums\Schema;
use Gemini\Enums\DataType;

$files = $client->files();
echo "Uploading\n";
$meta = $files->upload(
filename: 'document.pdf',
mimeType: MimeType::APPLICATION_PDF,
displayName: 'Document for search'
);
echo "Processing";
do {
echo ".";
sleep(2);
$meta = $files->metadataGet($meta->uri);
} while (! $meta->state->complete());
echo "\n";

if ($meta->state == FileState::Failed) {
die("Upload failed:\n".json_encode($meta->toArray(), JSON_PRETTY_PRINT));
}

$fileSearchStore = $client->fileSearchStores()->create(
displayName: 'My Search Store',
);

echo "File search store created: {$fileSearchStore->name}\n";
```

#### Get File Search Store
Get a specific file search store by name.

```php
$fileSearchStore = $client->fileSearchStores()->get('fileSearchStores/my-search-store');

echo "Name: {$fileSearchStore->name}\n";
echo "Display Name: {$fileSearchStore->displayName}\n";
```

#### List File Search Stores
List all file search stores.

```php
$response = $client->fileSearchStores()->list(pageSize: 10);

foreach ($response->fileSearchStores as $fileSearchStore) {
echo "Name: {$fileSearchStore->name}\n";
echo "Display Name: {$fileSearchStore->displayName}\n";
echo "--- \n";
}
```

#### Delete File Search Store
Delete a file search store by name.

```php
$client->fileSearchStores()->delete('fileSearchStores/my-search-store');
```

### File Search Documents

#### Upload File Search Document
Upload a local file directly to a file search store.

```php
use Gemini\Enums\MimeType;

$response = $client->fileSearchStores()->upload(
storeName: 'fileSearchStores/my-search-store',
filename: 'document2.pdf',
mimeType: MimeType::APPLICATION_PDF,
displayName: 'Another Search Document'
);

echo "File search document upload operation: {$response->name}\n";
```

#### Get File Search Document
Get a specific file search document by name.

```php
$fileSearchDocument = $client->fileSearchStores()->getDocument('fileSearchStores/my-search-store/fileSearchDocuments/my-document');

echo "Name: {$fileSearchDocument->name}\n";
echo "Display Name: {$fileSearchDocument->displayName}\n";
```

#### List File Search Documents
List all file search documents within a store.

```php
$response = $client->fileSearchStores()->listDocuments(storeName: 'fileSearchStores/my-search-store', pageSize: 10);

foreach ($response->documents as $fileSearchDocument) {
echo "Name: {$fileSearchDocument->name}\n";
echo "Display Name: {$fileSearchDocument->displayName}\n";
echo "Create Time: {$fileSearchDocument->createTime}\n";
echo "Update Time: {$fileSearchDocument->updateTime}\n";
echo "--- \n";
}
```

#### Delete File Search Document
Delete a file search document by name.

```php
$client->fileSearchStores()->deleteDocument('fileSearchStores/my-search-store/fileSearchDocuments/my-document');
```

### Embedding Resource
Embedding is a technique used to represent information as a list of floating point numbers in an array. With Gemini, you can represent text (words, sentences, and blocks of text) in a vectorized form, making it easier to compare and contrast embeddings. For example, two texts that share a similar subject matter or sentiment should have similar embeddings, which can be identified through mathematical comparison techniques such as cosine similarity.

Expand Down
7 changes: 7 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
use Gemini\Contracts\ClientContract;
use Gemini\Contracts\Resources\CachedContentsContract;
use Gemini\Contracts\Resources\FilesContract;
use Gemini\Contracts\Resources\FileSearchStoresContract;
use Gemini\Contracts\Resources\GenerativeModelContract;
use Gemini\Contracts\TransporterContract;
use Gemini\Enums\ModelType;
use Gemini\Resources\CachedContents;
use Gemini\Resources\ChatSession;
use Gemini\Resources\EmbeddingModel;
use Gemini\Resources\Files;
use Gemini\Resources\FileSearchStores;
use Gemini\Resources\GenerativeModel;
use Gemini\Resources\Models;

Expand Down Expand Up @@ -81,4 +83,9 @@ public function cachedContents(): CachedContentsContract
{
return new CachedContents($this->transporter);
}

public function fileSearchStores(): FileSearchStoresContract
{
return new FileSearchStores($this->transporter);
}
}
3 changes: 3 additions & 0 deletions src/Contracts/ClientContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Gemini\Contracts\Resources\ChatSessionContract;
use Gemini\Contracts\Resources\EmbeddingModalContract;
use Gemini\Contracts\Resources\FilesContract;
use Gemini\Contracts\Resources\FileSearchStoresContract;
use Gemini\Contracts\Resources\GenerativeModelContract;
use Gemini\Contracts\Resources\ModelContract;

Expand All @@ -35,4 +36,6 @@ public function chat(BackedEnum|string $model): ChatSessionContract;
public function files(): FilesContract;

public function cachedContents(): CachedContentsContract;

public function fileSearchStores(): FileSearchStoresContract;
}
73 changes: 73 additions & 0 deletions src/Contracts/Resources/FileSearchStoresContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace Gemini\Contracts\Resources;

use Gemini\Enums\MimeType;
use Gemini\Responses\FileSearchStores\Documents\DocumentResponse;
use Gemini\Responses\FileSearchStores\Documents\ListResponse as DocumentListResponse;
use Gemini\Responses\FileSearchStores\FileSearchStoreResponse;
use Gemini\Responses\FileSearchStores\ListResponse;
use Gemini\Responses\FileSearchStores\UploadResponse;

interface FileSearchStoresContract
{
/**
* Create a file search store.
*
* @see https://ai.google.dev/api/file-search/file-search-stores#method:-fileSearchStores.create
*/
public function create(?string $displayName = null): FileSearchStoreResponse;

/**
* Get a file search store.
*
* @see https://ai.google.dev/api/file-search/file-search-stores#method:-fileSearchStores.get
*/
public function get(string $name): FileSearchStoreResponse;

/**
* List file search stores.
*
* @see https://ai.google.dev/api/file-search/file-search-stores#method:-fileSearchStores.list
*/
public function list(?int $pageSize = null, ?string $nextPageToken = null): ListResponse;

/**
* Delete a file search store.
*
* @see https://ai.google.dev/api/file-search/file-search-stores#method:-fileSearchStores.delete
*/
public function delete(string $name, bool $force = false): void;

/**
* Upload a file to a file search store.
*
* @param array<string, string|int|float|array<string>> $customMetadata
*
* @see https://ai.google.dev/api/file-search/file-search-stores#method:-media.uploadtofilesearchstore
*/
public function upload(string $storeName, string $filename, ?MimeType $mimeType = null, ?string $displayName = null, array $customMetadata = []): UploadResponse;

/**
* List documents in a file search store.
*
* @see https://ai.google.dev/api/file-search/documents#method:-fileSearchStores.documents.list
*/
public function listDocuments(string $storeName, ?int $pageSize = null, ?string $nextPageToken = null): DocumentListResponse;

/**
* Get a document.
*
* @see https://ai.google.dev/api/file-search/documents#method:-fileSearchStores.documents.get
*/
public function getDocument(string $name): DocumentResponse;

/**
* Delete a document.
*
* @see https://ai.google.dev/api/file-search/documents#method:-fileSearchStores.documents.delete
*/
public function deleteDocument(string $name, bool $force = false): void;
}
2 changes: 1 addition & 1 deletion src/Data/Candidate.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function __construct(
) {}

/**
* @param array{ content: ?array{ parts: array{ array{ text: ?string, inlineData: ?array{ mimeType: string, data: string }, fileData: ?array{ fileUri: string, mimeType: string }, functionCall: ?array{ name: string, args: array<string, mixed>|null }, functionResponse: ?array{ name: string, response: array<string, mixed> } } }, role: string }, finishReason: ?string, safetyRatings: ?array{ array{ category: string, probability: string, blocked: ?bool } }, citationMetadata: ?array{ citationSources: array{ array{ startIndex: int, endIndex: int, uri: ?string, license: ?string} } }, index: ?int, tokenCount: ?int, avgLogprobs: ?float, groundingAttributions: ?array<array{ sourceId: array{ groundingPassage?: array{ passageId: string, partIndex: int }, semanticRetrieverChunk?: array{ source: string, chunk: string } }, content: array{ parts: array{ array{ text: ?string, inlineData: ?array{ mimeType: string, data: string }, fileData: ?array{ fileUri: string, mimeType: string }, functionCall: ?array{ name: string, args: array<string, mixed>|null }, functionResponse: ?array{ name: string, response: array<string, mixed> } } }, role: string } }>, groundingMetadata?: array{ groundingChunks: ?array<array{ web: null|array{ title: ?string, uri: ?string } }>, groundingSupports: ?array<array{ groundingChunkIndices: array<int>|null, confidenceScores: array<float>|null, segment: ?array{ partIndex: ?int, startIndex: ?int, endIndex: ?int, text: ?string } }>, webSearchQueries: ?array<string>, searchEntryPoint?: array{ renderedContent?: string|null, sdkBlob?: string|null }, retrievalMetadata: ?array{ googleSearchDynamicRetrievalScore?: float|null } }, logprobsResult?: array{ topCandidates: array<array{ candidates: array<array{ token: string, tokenId: int, logProbability: float }> }>, chosenCandidates: array<array{ token: string, tokenId: int, logProbability: float }> }, urlRetrievalMetadata?: array{ urlRetrievalContexts: array<array{ retrievedUrl: string }> } } $attributes
* @param array{ content: ?array{ parts: array{ array{ text: ?string, inlineData: ?array{ mimeType: string, data: string }, fileData: ?array{ fileUri: string, mimeType: string }, functionCall: ?array{ name: string, args: array<string, mixed>|null }, functionResponse: ?array{ name: string, response: array<string, mixed> } } }, role: string }, finishReason: ?string, safetyRatings: ?array{ array{ category: string, probability: string, blocked: ?bool } }, citationMetadata: ?array{ citationSources: array{ array{ startIndex: int, endIndex: int, uri: ?string, license: ?string} } }, index: ?int, tokenCount: ?int, avgLogprobs: ?float, groundingAttributions: ?array<array{ sourceId: array{ groundingPassage?: array{ passageId: string, partIndex: int }, semanticRetrieverChunk?: array{ source: string, chunk: string } }, content: array{ parts: array{ array{ text: ?string, inlineData: ?array{ mimeType: string, data: string }, fileData: ?array{ fileUri: string, mimeType: string }, functionCall: ?array{ name: string, args: array<string, mixed>|null }, functionResponse: ?array{ name: string, response: array<string, mixed> } } }, role: string } }>, groundingMetadata?: array{ groundingChunks: ?array<array{ web: null|array{ title: ?string, uri: ?string }, retrievedContext: null|array{ uri: ?string, title: ?string, text: ?string, fileSearchStore: ?string }, maps: null|array{ uri: ?string, title: ?string, text: ?string, placeId: ?string, placeAnswerSources: ?array{ reviewSnippets: array<array{title: ?string, googleMapsUri: ?string, reviewId: ?string}> } } }>, groundingSupports: ?array<array{ groundingChunkIndices: array<int>|null, confidenceScores: array<float>|null, segment: ?array{ partIndex: ?int, startIndex: ?int, endIndex: ?int, text: ?string } }>, webSearchQueries: ?array<string>, searchEntryPoint?: array{ renderedContent?: string|null, sdkBlob?: string|null }, retrievalMetadata: ?array{ googleSearchDynamicRetrievalScore?: float|null } }, logprobsResult?: array{ topCandidates: array<array{ candidates: array<array{ token: string, tokenId: int, logProbability: float }> }>, chosenCandidates: array<array{ token: string, tokenId: int, logProbability: float }> }, urlRetrievalMetadata?: array{ urlRetrievalContexts: array<array{ retrievedUrl: string }> } } $attributes
*/
public static function from(array $attributes): self
{
Expand Down
46 changes: 46 additions & 0 deletions src/Data/FileSearch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Gemini\Data;

use Gemini\Contracts\Arrayable;

/**
* FileSearch tool type. Tool to support File Search in Model.
*/
final class FileSearch implements Arrayable
{
/**
* @param array<string> $fileSearchStoreNames Required. The file search store names.
* @param string|null $metadataFilter Optional. A filter for metadata.
*/
public function __construct(
public readonly array $fileSearchStoreNames,
public readonly ?string $metadataFilter = null,
) {}

/**
* @param array{ fileSearchStoreNames: array<string>, metadataFilter?: string } $attributes
*/
public static function from(array $attributes): self
{
return new self(
fileSearchStoreNames: $attributes['fileSearchStoreNames'],
metadataFilter: $attributes['metadataFilter'] ?? null,
);
}

public function toArray(): array
{
$data = [
'fileSearchStoreNames' => $this->fileSearchStoreNames,
];

if ($this->metadataFilter !== null) {
$data['metadataFilter'] = $this->metadataFilter;
}

return $data;
}
}
Loading