llm-gemini 0.13.1__tar.gz → 0.14.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/PKG-INFO +14 -3
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/README.md +12 -2
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/llm_gemini.egg-info/PKG-INFO +14 -3
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/llm_gemini.egg-info/requires.txt +1 -0
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/llm_gemini.py +31 -11
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/pyproject.toml +6 -2
- llm_gemini-0.14.1/tests/test_gemini.py +212 -0
- llm_gemini-0.13.1/tests/test_gemini.py +0 -104
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/LICENSE +0 -0
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/llm_gemini.egg-info/SOURCES.txt +0 -0
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/llm_gemini.egg-info/dependency_links.txt +0 -0
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/llm_gemini.egg-info/entry_points.txt +0 -0
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/llm_gemini.egg-info/top_level.txt +0 -0
- {llm_gemini-0.13.1 → llm_gemini-0.14.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: llm-gemini
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.14.1
|
4
4
|
Summary: LLM plugin to access Google's Gemini family of models
|
5
5
|
Author: Simon Willison
|
6
6
|
License: Apache-2.0
|
@@ -17,6 +17,7 @@ Requires-Dist: ijson
|
|
17
17
|
Provides-Extra: test
|
18
18
|
Requires-Dist: pytest; extra == "test"
|
19
19
|
Requires-Dist: pytest-recording; extra == "test"
|
20
|
+
Requires-Dist: pytest-asyncio; extra == "test"
|
20
21
|
Requires-Dist: nest-asyncio; extra == "test"
|
21
22
|
|
22
23
|
# llm-gemini
|
@@ -145,7 +146,7 @@ llm chat -m gemini-1.5-pro-latest
|
|
145
146
|
|
146
147
|
## Embeddings
|
147
148
|
|
148
|
-
The plugin also adds support for the `text-embedding-004` embedding
|
149
|
+
The plugin also adds support for the `gemini-embedding-exp-03-07` and `text-embedding-004` embedding models.
|
149
150
|
|
150
151
|
Run that against a single string like this:
|
151
152
|
```bash
|
@@ -153,10 +154,20 @@ llm embed -m text-embedding-004 -c 'hello world'
|
|
153
154
|
```
|
154
155
|
This returns a JSON array of 768 numbers.
|
155
156
|
|
157
|
+
The `gemini-embedding-exp-03-07` model is larger, returning 3072 numbers. You can also use variants of it that are truncated down to smaller sizes:
|
158
|
+
|
159
|
+
- `gemini-embedding-exp-03-07` - 3072 numbers
|
160
|
+
- `gemini-embedding-exp-03-07-2048` - 2048 numbers
|
161
|
+
- `gemini-embedding-exp-03-07-1024` - 1024 numbers
|
162
|
+
- `gemini-embedding-exp-03-07-512` - 512 numbers
|
163
|
+
- `gemini-embedding-exp-03-07-256` - 256 numbers
|
164
|
+
- `gemini-embedding-exp-03-07-128` - 128 numbers
|
165
|
+
|
156
166
|
This command will embed every `README.md` file in child directories of the current directory and store the results in a SQLite database called `embed.db` in a collection called `readmes`:
|
157
167
|
|
158
168
|
```bash
|
159
|
-
llm embed-multi readmes
|
169
|
+
llm embed-multi readmes -d embed.db -m gemini-embedding-exp-03-07-128 \
|
170
|
+
--files . '*/README.md'
|
160
171
|
```
|
161
172
|
You can then run similarity searches against that collection like this:
|
162
173
|
```bash
|
@@ -124,7 +124,7 @@ llm chat -m gemini-1.5-pro-latest
|
|
124
124
|
|
125
125
|
## Embeddings
|
126
126
|
|
127
|
-
The plugin also adds support for the `text-embedding-004` embedding
|
127
|
+
The plugin also adds support for the `gemini-embedding-exp-03-07` and `text-embedding-004` embedding models.
|
128
128
|
|
129
129
|
Run that against a single string like this:
|
130
130
|
```bash
|
@@ -132,10 +132,20 @@ llm embed -m text-embedding-004 -c 'hello world'
|
|
132
132
|
```
|
133
133
|
This returns a JSON array of 768 numbers.
|
134
134
|
|
135
|
+
The `gemini-embedding-exp-03-07` model is larger, returning 3072 numbers. You can also use variants of it that are truncated down to smaller sizes:
|
136
|
+
|
137
|
+
- `gemini-embedding-exp-03-07` - 3072 numbers
|
138
|
+
- `gemini-embedding-exp-03-07-2048` - 2048 numbers
|
139
|
+
- `gemini-embedding-exp-03-07-1024` - 1024 numbers
|
140
|
+
- `gemini-embedding-exp-03-07-512` - 512 numbers
|
141
|
+
- `gemini-embedding-exp-03-07-256` - 256 numbers
|
142
|
+
- `gemini-embedding-exp-03-07-128` - 128 numbers
|
143
|
+
|
135
144
|
This command will embed every `README.md` file in child directories of the current directory and store the results in a SQLite database called `embed.db` in a collection called `readmes`:
|
136
145
|
|
137
146
|
```bash
|
138
|
-
llm embed-multi readmes
|
147
|
+
llm embed-multi readmes -d embed.db -m gemini-embedding-exp-03-07-128 \
|
148
|
+
--files . '*/README.md'
|
139
149
|
```
|
140
150
|
You can then run similarity searches against that collection like this:
|
141
151
|
```bash
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: llm-gemini
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.14.1
|
4
4
|
Summary: LLM plugin to access Google's Gemini family of models
|
5
5
|
Author: Simon Willison
|
6
6
|
License: Apache-2.0
|
@@ -17,6 +17,7 @@ Requires-Dist: ijson
|
|
17
17
|
Provides-Extra: test
|
18
18
|
Requires-Dist: pytest; extra == "test"
|
19
19
|
Requires-Dist: pytest-recording; extra == "test"
|
20
|
+
Requires-Dist: pytest-asyncio; extra == "test"
|
20
21
|
Requires-Dist: nest-asyncio; extra == "test"
|
21
22
|
|
22
23
|
# llm-gemini
|
@@ -145,7 +146,7 @@ llm chat -m gemini-1.5-pro-latest
|
|
145
146
|
|
146
147
|
## Embeddings
|
147
148
|
|
148
|
-
The plugin also adds support for the `text-embedding-004` embedding
|
149
|
+
The plugin also adds support for the `gemini-embedding-exp-03-07` and `text-embedding-004` embedding models.
|
149
150
|
|
150
151
|
Run that against a single string like this:
|
151
152
|
```bash
|
@@ -153,10 +154,20 @@ llm embed -m text-embedding-004 -c 'hello world'
|
|
153
154
|
```
|
154
155
|
This returns a JSON array of 768 numbers.
|
155
156
|
|
157
|
+
The `gemini-embedding-exp-03-07` model is larger, returning 3072 numbers. You can also use variants of it that are truncated down to smaller sizes:
|
158
|
+
|
159
|
+
- `gemini-embedding-exp-03-07` - 3072 numbers
|
160
|
+
- `gemini-embedding-exp-03-07-2048` - 2048 numbers
|
161
|
+
- `gemini-embedding-exp-03-07-1024` - 1024 numbers
|
162
|
+
- `gemini-embedding-exp-03-07-512` - 512 numbers
|
163
|
+
- `gemini-embedding-exp-03-07-256` - 256 numbers
|
164
|
+
- `gemini-embedding-exp-03-07-128` - 128 numbers
|
165
|
+
|
156
166
|
This command will embed every `README.md` file in child directories of the current directory and store the results in a SQLite database called `embed.db` in a collection called `readmes`:
|
157
167
|
|
158
168
|
```bash
|
159
|
-
llm embed-multi readmes
|
169
|
+
llm embed-multi readmes -d embed.db -m gemini-embedding-exp-03-07-128 \
|
170
|
+
--files . '*/README.md'
|
160
171
|
```
|
161
172
|
You can then run similarity searches against that collection like this:
|
162
173
|
```bash
|
@@ -88,18 +88,24 @@ def resolve_type(attachment):
|
|
88
88
|
return mime_type
|
89
89
|
|
90
90
|
|
91
|
-
def cleanup_schema(schema):
|
91
|
+
def cleanup_schema(schema, in_properties=False):
|
92
92
|
"Gemini supports only a subset of JSON schema"
|
93
93
|
keys_to_remove = ("$schema", "additionalProperties", "title")
|
94
|
-
|
94
|
+
|
95
95
|
if isinstance(schema, dict):
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
96
|
+
# Only remove keys if we're not inside a 'properties' block.
|
97
|
+
if not in_properties:
|
98
|
+
for key in keys_to_remove:
|
99
|
+
schema.pop(key, None)
|
100
|
+
for key, value in list(schema.items()):
|
101
|
+
# If the key is 'properties', set the flag for its value.
|
102
|
+
if key == "properties" and isinstance(value, dict):
|
103
|
+
cleanup_schema(value, in_properties=True)
|
104
|
+
else:
|
105
|
+
cleanup_schema(value, in_properties=in_properties)
|
100
106
|
elif isinstance(schema, list):
|
101
|
-
for
|
102
|
-
cleanup_schema(
|
107
|
+
for item in schema:
|
108
|
+
cleanup_schema(item, in_properties=in_properties)
|
103
109
|
return schema
|
104
110
|
|
105
111
|
|
@@ -378,9 +384,19 @@ class AsyncGeminiPro(_SharedGemini, llm.AsyncKeyModel):
|
|
378
384
|
|
379
385
|
@llm.hookimpl
|
380
386
|
def register_embedding_models(register):
|
387
|
+
register(GeminiEmbeddingModel("text-embedding-004", "text-embedding-004"))
|
388
|
+
# gemini-embedding-exp-03-07 in different truncation sizes
|
381
389
|
register(
|
382
|
-
GeminiEmbeddingModel(
|
390
|
+
GeminiEmbeddingModel(
|
391
|
+
"gemini-embedding-exp-03-07", "gemini-embedding-exp-03-07"
|
392
|
+
),
|
383
393
|
)
|
394
|
+
for i in (128, 256, 512, 1024, 2048):
|
395
|
+
register(
|
396
|
+
GeminiEmbeddingModel(
|
397
|
+
f"gemini-embedding-exp-03-07-{i}", f"gemini-embedding-exp-03-07", i
|
398
|
+
),
|
399
|
+
)
|
384
400
|
|
385
401
|
|
386
402
|
class GeminiEmbeddingModel(llm.EmbeddingModel):
|
@@ -388,9 +404,10 @@ class GeminiEmbeddingModel(llm.EmbeddingModel):
|
|
388
404
|
key_env_var = "LLM_GEMINI_KEY"
|
389
405
|
batch_size = 20
|
390
406
|
|
391
|
-
def __init__(self, model_id, gemini_model_id):
|
407
|
+
def __init__(self, model_id, gemini_model_id, truncate=None):
|
392
408
|
self.model_id = model_id
|
393
409
|
self.gemini_model_id = gemini_model_id
|
410
|
+
self.truncate = truncate
|
394
411
|
|
395
412
|
def embed_batch(self, items):
|
396
413
|
headers = {
|
@@ -416,4 +433,7 @@ class GeminiEmbeddingModel(llm.EmbeddingModel):
|
|
416
433
|
)
|
417
434
|
|
418
435
|
response.raise_for_status()
|
419
|
-
|
436
|
+
values = [item["values"] for item in response.json()["embeddings"]]
|
437
|
+
if self.truncate:
|
438
|
+
values = [value[: self.truncate] for value in values]
|
439
|
+
return values
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "llm-gemini"
|
3
|
-
version = "0.
|
3
|
+
version = "0.14.1"
|
4
4
|
description = "LLM plugin to access Google's Gemini family of models"
|
5
5
|
readme = "README.md"
|
6
6
|
authors = [{name = "Simon Willison"}]
|
@@ -24,4 +24,8 @@ CI = "https://github.com/simonw/llm-gemini/actions"
|
|
24
24
|
gemini = "llm_gemini"
|
25
25
|
|
26
26
|
[project.optional-dependencies]
|
27
|
-
test = ["pytest", "pytest-recording", "nest-asyncio"]
|
27
|
+
test = ["pytest", "pytest-recording", "pytest-asyncio", "nest-asyncio"]
|
28
|
+
|
29
|
+
[tool.pytest.ini_options]
|
30
|
+
asyncio_mode = "strict"
|
31
|
+
asyncio_default_fixture_loop_scope = "function"
|
@@ -0,0 +1,212 @@
|
|
1
|
+
import llm
|
2
|
+
import nest_asyncio
|
3
|
+
import json
|
4
|
+
import os
|
5
|
+
import pytest
|
6
|
+
import pydantic
|
7
|
+
from llm_gemini import cleanup_schema
|
8
|
+
|
9
|
+
nest_asyncio.apply()
|
10
|
+
|
11
|
+
GEMINI_API_KEY = os.environ.get("PYTEST_GEMINI_API_KEY", None) or "gm-..."
|
12
|
+
|
13
|
+
|
14
|
+
@pytest.mark.vcr
|
15
|
+
@pytest.mark.asyncio
|
16
|
+
async def test_prompt():
|
17
|
+
model = llm.get_model("gemini-1.5-flash-latest")
|
18
|
+
response = model.prompt("Name for a pet pelican, just the name", key=GEMINI_API_KEY)
|
19
|
+
assert str(response) == "Percy\n"
|
20
|
+
assert response.response_json == {
|
21
|
+
"candidates": [
|
22
|
+
{
|
23
|
+
"finishReason": "STOP",
|
24
|
+
"safetyRatings": [
|
25
|
+
{
|
26
|
+
"category": "HARM_CATEGORY_HATE_SPEECH",
|
27
|
+
"probability": "NEGLIGIBLE",
|
28
|
+
},
|
29
|
+
{
|
30
|
+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
31
|
+
"probability": "NEGLIGIBLE",
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"category": "HARM_CATEGORY_HARASSMENT",
|
35
|
+
"probability": "NEGLIGIBLE",
|
36
|
+
},
|
37
|
+
{
|
38
|
+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
39
|
+
"probability": "NEGLIGIBLE",
|
40
|
+
},
|
41
|
+
],
|
42
|
+
}
|
43
|
+
],
|
44
|
+
"modelVersion": "gemini-1.5-flash-latest",
|
45
|
+
}
|
46
|
+
assert response.token_details == {
|
47
|
+
"promptTokensDetails": [{"modality": "TEXT", "tokenCount": 9}],
|
48
|
+
"candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 2}],
|
49
|
+
}
|
50
|
+
assert response.input_tokens == 9
|
51
|
+
assert response.output_tokens == 2
|
52
|
+
|
53
|
+
# And try it async too
|
54
|
+
async_model = llm.get_async_model("gemini-1.5-flash-latest")
|
55
|
+
response = await async_model.prompt(
|
56
|
+
"Name for a pet pelican, just the name", key=GEMINI_API_KEY
|
57
|
+
)
|
58
|
+
text = await response.text()
|
59
|
+
assert text == "Percy\n"
|
60
|
+
|
61
|
+
|
62
|
+
@pytest.mark.vcr
|
63
|
+
@pytest.mark.asyncio
|
64
|
+
async def test_prompt_with_pydantic_schema():
|
65
|
+
class Dog(pydantic.BaseModel):
|
66
|
+
name: str
|
67
|
+
age: int
|
68
|
+
bio: str
|
69
|
+
|
70
|
+
model = llm.get_model("gemini-1.5-flash-latest")
|
71
|
+
response = model.prompt(
|
72
|
+
"Invent a cool dog", key=GEMINI_API_KEY, schema=Dog, stream=False
|
73
|
+
)
|
74
|
+
assert json.loads(response.text()) == {
|
75
|
+
"age": 3,
|
76
|
+
"bio": "A fluffy Samoyed with exceptional intelligence and a love for belly rubs. He's mastered several tricks, including fetching the newspaper and opening doors.",
|
77
|
+
"name": "Cloud",
|
78
|
+
}
|
79
|
+
assert response.response_json == {
|
80
|
+
"candidates": [
|
81
|
+
{
|
82
|
+
"finishReason": "STOP",
|
83
|
+
"safetyRatings": [
|
84
|
+
{
|
85
|
+
"category": "HARM_CATEGORY_HATE_SPEECH",
|
86
|
+
"probability": "NEGLIGIBLE",
|
87
|
+
},
|
88
|
+
{
|
89
|
+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
90
|
+
"probability": "NEGLIGIBLE",
|
91
|
+
},
|
92
|
+
{
|
93
|
+
"category": "HARM_CATEGORY_HARASSMENT",
|
94
|
+
"probability": "NEGLIGIBLE",
|
95
|
+
},
|
96
|
+
{
|
97
|
+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
98
|
+
"probability": "NEGLIGIBLE",
|
99
|
+
},
|
100
|
+
],
|
101
|
+
}
|
102
|
+
],
|
103
|
+
"modelVersion": "gemini-1.5-flash-latest",
|
104
|
+
}
|
105
|
+
assert response.input_tokens == 10
|
106
|
+
|
107
|
+
|
108
|
+
@pytest.mark.vcr
|
109
|
+
@pytest.mark.parametrize(
|
110
|
+
"model_id",
|
111
|
+
(
|
112
|
+
"gemini-embedding-exp-03-07",
|
113
|
+
"gemini-embedding-exp-03-07-128",
|
114
|
+
"gemini-embedding-exp-03-07-512",
|
115
|
+
),
|
116
|
+
)
|
117
|
+
def test_embedding(model_id, monkeypatch):
|
118
|
+
monkeypatch.setenv("LLM_GEMINI_KEY", GEMINI_API_KEY)
|
119
|
+
model = llm.get_embedding_model(model_id)
|
120
|
+
response = model.embed("Some text goes here")
|
121
|
+
expected_length = 3072
|
122
|
+
if model_id.endswith("-128"):
|
123
|
+
expected_length = 128
|
124
|
+
elif model_id.endswith("-512"):
|
125
|
+
expected_length = 512
|
126
|
+
assert len(response) == expected_length
|
127
|
+
|
128
|
+
|
129
|
+
@pytest.mark.parametrize(
|
130
|
+
"schema,expected",
|
131
|
+
[
|
132
|
+
# Test 1: Top-level keys removal
|
133
|
+
(
|
134
|
+
{
|
135
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
136
|
+
"title": "Example Schema",
|
137
|
+
"additionalProperties": False,
|
138
|
+
"type": "object",
|
139
|
+
},
|
140
|
+
{"type": "object"},
|
141
|
+
),
|
142
|
+
# Test 2: Preserve keys within a "properties" block
|
143
|
+
(
|
144
|
+
{
|
145
|
+
"type": "object",
|
146
|
+
"properties": {
|
147
|
+
"authors": {"type": "string"},
|
148
|
+
"title": {"type": "string"},
|
149
|
+
"reference": {"type": "string"},
|
150
|
+
"year": {"type": "string"},
|
151
|
+
},
|
152
|
+
"title": "This should be removed from the top-level",
|
153
|
+
},
|
154
|
+
{
|
155
|
+
"type": "object",
|
156
|
+
"properties": {
|
157
|
+
"authors": {"type": "string"},
|
158
|
+
"title": {"type": "string"},
|
159
|
+
"reference": {"type": "string"},
|
160
|
+
"year": {"type": "string"},
|
161
|
+
},
|
162
|
+
},
|
163
|
+
),
|
164
|
+
# Test 3: Nested keys outside and inside properties block
|
165
|
+
(
|
166
|
+
{
|
167
|
+
"definitions": {
|
168
|
+
"info": {
|
169
|
+
"title": "Info title", # should be removed because it's not inside a "properties" block
|
170
|
+
"description": "A description",
|
171
|
+
"properties": {
|
172
|
+
"name": {
|
173
|
+
"title": "Name Title",
|
174
|
+
"type": "string",
|
175
|
+
}, # title here should be preserved
|
176
|
+
"$schema": {
|
177
|
+
"type": "string"
|
178
|
+
}, # should be preserved as it's within properties
|
179
|
+
},
|
180
|
+
}
|
181
|
+
},
|
182
|
+
"$schema": "http://example.com/schema",
|
183
|
+
},
|
184
|
+
{
|
185
|
+
"definitions": {
|
186
|
+
"info": {
|
187
|
+
"description": "A description",
|
188
|
+
"properties": {
|
189
|
+
"name": {"title": "Name Title", "type": "string"},
|
190
|
+
"$schema": {"type": "string"},
|
191
|
+
},
|
192
|
+
}
|
193
|
+
}
|
194
|
+
},
|
195
|
+
),
|
196
|
+
# Test 4: List of schemas
|
197
|
+
(
|
198
|
+
[
|
199
|
+
{
|
200
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
201
|
+
"type": "object",
|
202
|
+
},
|
203
|
+
{"title": "Should be removed", "type": "array"},
|
204
|
+
],
|
205
|
+
[{"type": "object"}, {"type": "array"}],
|
206
|
+
),
|
207
|
+
],
|
208
|
+
)
|
209
|
+
def test_cleanup_schema(schema, expected):
|
210
|
+
# Use a deep copy so the original test data remains unchanged.
|
211
|
+
result = cleanup_schema(schema)
|
212
|
+
assert result == expected
|
@@ -1,104 +0,0 @@
|
|
1
|
-
import llm
|
2
|
-
import nest_asyncio
|
3
|
-
import json
|
4
|
-
import os
|
5
|
-
import pytest
|
6
|
-
import pydantic
|
7
|
-
|
8
|
-
nest_asyncio.apply()
|
9
|
-
|
10
|
-
GEMINI_API_KEY = os.environ.get("PYTEST_GEMINI_API_KEY", None) or "gm-..."
|
11
|
-
|
12
|
-
|
13
|
-
@pytest.mark.vcr
|
14
|
-
@pytest.mark.asyncio
|
15
|
-
async def test_prompt():
|
16
|
-
model = llm.get_model("gemini-1.5-flash-latest")
|
17
|
-
response = model.prompt("Name for a pet pelican, just the name", key=GEMINI_API_KEY)
|
18
|
-
assert str(response) == "Percy\n"
|
19
|
-
assert response.response_json == {
|
20
|
-
"candidates": [
|
21
|
-
{
|
22
|
-
"finishReason": "STOP",
|
23
|
-
"safetyRatings": [
|
24
|
-
{
|
25
|
-
"category": "HARM_CATEGORY_HATE_SPEECH",
|
26
|
-
"probability": "NEGLIGIBLE",
|
27
|
-
},
|
28
|
-
{
|
29
|
-
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
30
|
-
"probability": "NEGLIGIBLE",
|
31
|
-
},
|
32
|
-
{
|
33
|
-
"category": "HARM_CATEGORY_HARASSMENT",
|
34
|
-
"probability": "NEGLIGIBLE",
|
35
|
-
},
|
36
|
-
{
|
37
|
-
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
38
|
-
"probability": "NEGLIGIBLE",
|
39
|
-
},
|
40
|
-
],
|
41
|
-
}
|
42
|
-
],
|
43
|
-
"modelVersion": "gemini-1.5-flash-latest",
|
44
|
-
}
|
45
|
-
assert response.token_details == {
|
46
|
-
"promptTokensDetails": [{"modality": "TEXT", "tokenCount": 9}],
|
47
|
-
"candidatesTokensDetails": [{"modality": "TEXT", "tokenCount": 2}],
|
48
|
-
}
|
49
|
-
assert response.input_tokens == 9
|
50
|
-
assert response.output_tokens == 2
|
51
|
-
|
52
|
-
# And try it async too
|
53
|
-
async_model = llm.get_async_model("gemini-1.5-flash-latest")
|
54
|
-
response = await async_model.prompt(
|
55
|
-
"Name for a pet pelican, just the name", key=GEMINI_API_KEY
|
56
|
-
)
|
57
|
-
text = await response.text()
|
58
|
-
assert text == "Percy\n"
|
59
|
-
|
60
|
-
|
61
|
-
@pytest.mark.vcr
|
62
|
-
@pytest.mark.asyncio
|
63
|
-
async def test_prompt_with_pydantic_schema():
|
64
|
-
class Dog(pydantic.BaseModel):
|
65
|
-
name: str
|
66
|
-
age: int
|
67
|
-
bio: str
|
68
|
-
|
69
|
-
model = llm.get_model("gemini-1.5-flash-latest")
|
70
|
-
response = model.prompt(
|
71
|
-
"Invent a cool dog", key=GEMINI_API_KEY, schema=Dog, stream=False
|
72
|
-
)
|
73
|
-
assert json.loads(response.text()) == {
|
74
|
-
"age": 3,
|
75
|
-
"bio": "A fluffy Samoyed with exceptional intelligence and a love for belly rubs. He's mastered several tricks, including fetching the newspaper and opening doors.",
|
76
|
-
"name": "Cloud",
|
77
|
-
}
|
78
|
-
assert response.response_json == {
|
79
|
-
"candidates": [
|
80
|
-
{
|
81
|
-
"finishReason": "STOP",
|
82
|
-
"safetyRatings": [
|
83
|
-
{
|
84
|
-
"category": "HARM_CATEGORY_HATE_SPEECH",
|
85
|
-
"probability": "NEGLIGIBLE",
|
86
|
-
},
|
87
|
-
{
|
88
|
-
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
89
|
-
"probability": "NEGLIGIBLE",
|
90
|
-
},
|
91
|
-
{
|
92
|
-
"category": "HARM_CATEGORY_HARASSMENT",
|
93
|
-
"probability": "NEGLIGIBLE",
|
94
|
-
},
|
95
|
-
{
|
96
|
-
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
97
|
-
"probability": "NEGLIGIBLE",
|
98
|
-
},
|
99
|
-
],
|
100
|
-
}
|
101
|
-
],
|
102
|
-
"modelVersion": "gemini-1.5-flash-latest",
|
103
|
-
}
|
104
|
-
assert response.input_tokens == 10
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|