axio-transport-google 0.9.2__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.
@@ -0,0 +1,14 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ .mypy_cache/
6
+ .ruff_cache/
7
+ .pytest_cache/
8
+ *.egg-info/
9
+ dist/
10
+ build/
11
+ _build/
12
+ .DS_Store
13
+ *.sqlite
14
+ *.db
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: axio-transport-google
3
+ Version: 0.9.2
4
+ Summary: Google GenAI (Gemini) transport for Axio with image and video support
5
+ Project-URL: Documentation, https://docs.axio-agent.com
6
+ Project-URL: Homepage, https://github.com/mosquito/axio-agent
7
+ Project-URL: Repository, https://github.com/mosquito/axio-agent
8
+ License: MIT
9
+ Keywords: agent,ai,gemini,genai,google,llm,transport
10
+ Requires-Python: >=3.12
11
+ Requires-Dist: axio
12
+ Requires-Dist: axio-transport-anthropic[vertexai]
13
+ Requires-Dist: google-auth[urllib3]>=2.0
14
+ Provides-Extra: tui
15
+ Requires-Dist: textual>=2.1.0; extra == 'tui'
16
+ Description-Content-Type: text/markdown
17
+
18
+ # axio-transport-google
19
+
20
+ Google GenAI (Gemini) transport for Axio with image and video support.
21
+
22
+ ## API reference
23
+
24
+ Request/response types are generated from the Vertex AI discovery documents:
25
+
26
+ - **v1:** https://aiplatform.googleapis.com/$discovery/rest?version=v1
27
+ - **v1beta1:** https://aiplatform.googleapis.com/$discovery/rest?version=v1beta1
28
+
29
+ To regenerate TypedDict definitions after an API update:
30
+
31
+ ```bash
32
+ python scripts/generate_types.py [--version v1beta1]
33
+ ```
34
+
35
+ This produces `src/axio_transport_google/_generated_types.py`, which the transport
36
+ uses for type annotations. Conformance tests in `tests/test_generated_types.py`
37
+ validate that payload builders match the discovery schema.
@@ -0,0 +1,20 @@
1
+ # axio-transport-google
2
+
3
+ Google GenAI (Gemini) transport for Axio with image and video support.
4
+
5
+ ## API reference
6
+
7
+ Request/response types are generated from the Vertex AI discovery documents:
8
+
9
+ - **v1:** https://aiplatform.googleapis.com/$discovery/rest?version=v1
10
+ - **v1beta1:** https://aiplatform.googleapis.com/$discovery/rest?version=v1beta1
11
+
12
+ To regenerate TypedDict definitions after an API update:
13
+
14
+ ```bash
15
+ python scripts/generate_types.py [--version v1beta1]
16
+ ```
17
+
18
+ This produces `src/axio_transport_google/_generated_types.py`, which the transport
19
+ uses for type annotations. Conformance tests in `tests/test_generated_types.py`
20
+ validate that payload builders match the discovery schema.
@@ -0,0 +1,69 @@
1
+ [project]
2
+ name = "axio-transport-google"
3
+ version = "0.9.2"
4
+ description = "Google GenAI (Gemini) transport for Axio with image and video support"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = {text = "MIT"}
8
+ keywords = ["llm", "agent", "ai", "google", "gemini", "genai", "transport"]
9
+ dependencies = [
10
+ "axio",
11
+ "axio-transport-anthropic[vertexai]",
12
+ "google-auth[urllib3]>=2.0",
13
+ ]
14
+
15
+ [project.optional-dependencies]
16
+ tui = ["textual>=2.1.0"]
17
+
18
+ [project.entry-points."axio.transport"]
19
+ google = "axio_transport_google:GoogleTransport"
20
+ google-vertex = "axio_transport_google:VertexAITransport"
21
+
22
+ [project.entry-points."axio.transport.realtime"]
23
+ gemini = "axio_transport_google.realtime:GeminiLiveTransport"
24
+ vertex = "axio_transport_google.realtime:VertexLiveTransport"
25
+
26
+ [project.entry-points."axio.transport.settings"]
27
+ google = "axio_transport_google.tui:GoogleSettingsScreen"
28
+ google-vertex = "axio_transport_google.tui:VertexSettingsScreen"
29
+
30
+ [project.entry-points."axio.tools"]
31
+ generate_image = "axio_transport_google.tools:generate_image"
32
+ generate_video = "axio_transport_google.tools:generate_video"
33
+
34
+ [project.urls]
35
+ Documentation = "https://docs.axio-agent.com"
36
+ Homepage = "https://github.com/mosquito/axio-agent"
37
+ Repository = "https://github.com/mosquito/axio-agent"
38
+
39
+ [build-system]
40
+ requires = ["hatchling"]
41
+ build-backend = "hatchling.build"
42
+
43
+ [tool.hatch.build.targets.wheel]
44
+ packages = ["src/axio_transport_google"]
45
+ sources = ["src"]
46
+
47
+ [tool.pytest.ini_options]
48
+ asyncio_mode = "auto"
49
+
50
+ [tool.ruff]
51
+ line-length = 119
52
+ output-format = "concise"
53
+ target-version = "py312"
54
+
55
+ [tool.ruff.lint]
56
+ select = ["E", "F", "I", "UP"]
57
+
58
+ [tool.mypy]
59
+ strict = true
60
+ python_version = "3.12"
61
+
62
+ [dependency-groups]
63
+ dev = [
64
+ "pytest>=8",
65
+ "pytest-asyncio>=0.24",
66
+ "mypy>=1.14",
67
+ "ruff>=0.9",
68
+ "pytest-cov>=7.1.0",
69
+ ]
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env python3
2
+ """Generate TypedDict definitions from Google Vertex AI discovery document.
3
+
4
+ API reference (discovery docs):
5
+ https://aiplatform.googleapis.com/$discovery/rest?version=v1
6
+ https://aiplatform.googleapis.com/$discovery/rest?version=v1beta1
7
+
8
+ Usage:
9
+ python scripts/generate_types.py [--version v1beta1] [--output src/axio_transport_google/_generated_types.py]
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import json
16
+ import sys
17
+ import textwrap
18
+ import urllib.request
19
+ from typing import Any, cast
20
+
21
+ DISCOVERY_URL = "https://aiplatform.googleapis.com/$discovery/rest?version={version}"
22
+
23
+ # Schemas relevant to the generateContent API, in dependency order.
24
+ # Keys: short name used in generated code. Values: full discovery schema id.
25
+ SCHEMA_MAP = {
26
+ "Blob": "Blob",
27
+ "FileData": "FileData",
28
+ "FunctionCall": "FunctionCall",
29
+ "FunctionResponse": "FunctionResponse",
30
+ "FunctionResponsePart": "FunctionResponsePart",
31
+ "ExecutableCode": "ExecutableCode",
32
+ "CodeExecutionResult": "CodeExecutionResult",
33
+ "VideoMetadata": "VideoMetadata",
34
+ "Part": "Part",
35
+ "Content": "Content",
36
+ "FunctionDeclaration": "FunctionDeclaration",
37
+ "Tool": "Tool",
38
+ "FunctionCallingConfig": "FunctionCallingConfig",
39
+ "ToolConfig": "ToolConfig",
40
+ "ThinkingConfig": "GenerationConfigThinkingConfig",
41
+ "GenerationConfig": "GenerationConfig",
42
+ "SafetySetting": "SafetySetting",
43
+ "GenerateContentRequest": "GenerateContentRequest",
44
+ "Candidate": "Candidate",
45
+ "UsageMetadata": "GenerateContentResponseUsageMetadata",
46
+ "PromptFeedback": "GenerateContentResponsePromptFeedback",
47
+ "GenerateContentResponse": "GenerateContentResponse",
48
+ }
49
+
50
+ # Mapping from discovery type strings to Python type annotations
51
+ TYPE_MAP: dict[str, str] = {
52
+ "string": "str",
53
+ "integer": "int",
54
+ "number": "float",
55
+ "boolean": "bool",
56
+ "any": "Any",
57
+ }
58
+
59
+
60
+ def fetch_discovery(version: str) -> dict[str, Any]:
61
+ url = DISCOVERY_URL.format(version=version)
62
+ print(f"Fetching {url} ...", file=sys.stderr)
63
+ with urllib.request.urlopen(url) as resp:
64
+ return cast(dict[str, Any], json.loads(resp.read()))
65
+
66
+
67
+ def resolve_schema_name(full_id: str, version_prefix: str) -> str | None:
68
+ """Extract the short schema name from the full discovery ID."""
69
+ if full_id.startswith(version_prefix):
70
+ return full_id[len(version_prefix) :]
71
+ return None
72
+
73
+
74
+ def ref_to_short_name(ref: str, version_prefix: str) -> str | None:
75
+ """Convert a $ref value to our short TypedDict name, if it's in SCHEMA_MAP."""
76
+ short = resolve_schema_name(ref, version_prefix)
77
+ if short is None:
78
+ return None
79
+ for td_name, schema_suffix in SCHEMA_MAP.items():
80
+ if short == schema_suffix:
81
+ return td_name
82
+ return None
83
+
84
+
85
+ def python_type_for_property(prop: dict[str, Any], version_prefix: str, required: bool = True) -> str:
86
+ """Convert a discovery property definition to a Python type annotation."""
87
+ if "$ref" in prop:
88
+ td_name = ref_to_short_name(prop["$ref"], version_prefix)
89
+ if td_name:
90
+ return td_name
91
+ return "dict[str, Any]"
92
+
93
+ prop_type = prop.get("type", "any")
94
+
95
+ if prop_type == "array":
96
+ items = prop.get("items", {})
97
+ item_type = python_type_for_property(items, version_prefix)
98
+ return f"list[{item_type}]"
99
+
100
+ if prop_type == "object":
101
+ additional = prop.get("additionalProperties", {})
102
+ if additional:
103
+ val_type = python_type_for_property(additional, version_prefix)
104
+ return f"dict[str, {val_type}]"
105
+ return "dict[str, Any]"
106
+
107
+ if "enum" in prop:
108
+ literals = [f'"{v}"' for v in prop["enum"]]
109
+ single_line = f"Literal[{', '.join(literals)}]"
110
+ if len(single_line) <= 80:
111
+ return single_line
112
+ # Multi-line Literal for long enum lists
113
+ inner = ",\n ".join(literals)
114
+ return f"Literal[\n {inner},\n]"
115
+
116
+ return TYPE_MAP.get(prop_type, "Any")
117
+
118
+
119
+ def generate_typeddict(
120
+ td_name: str,
121
+ schema: dict[str, Any],
122
+ version_prefix: str,
123
+ ) -> str:
124
+ """Generate a TypedDict class definition from a discovery schema."""
125
+ properties = schema.get("properties", {})
126
+ if not properties:
127
+ return f"class {td_name}(TypedDict, total=False):\n pass\n"
128
+
129
+ lines = [f"class {td_name}(TypedDict, total=False):"]
130
+
131
+ desc = schema.get("description", "")
132
+ if desc:
133
+ wrapped = textwrap.fill(desc, width=100, initial_indent=" ", subsequent_indent=" ")
134
+ lines.append(f' """{wrapped[4:]}"""')
135
+ lines.append("")
136
+
137
+ for prop_name, prop_def in sorted(properties.items()):
138
+ py_type = python_type_for_property(prop_def, version_prefix)
139
+ prop_desc = prop_def.get("description", "")
140
+ if prop_desc:
141
+ short_desc = prop_desc.split(".")[0].strip()
142
+ if len(short_desc) > 90:
143
+ short_desc = short_desc[:87] + "..."
144
+ lines.append(f" # {short_desc}")
145
+ if "\n" in py_type:
146
+ # Multi-line type (e.g. long Literal) — indent to field level
147
+ indented = py_type.replace("\n", "\n ")
148
+ lines.append(f" {prop_name}: {indented}")
149
+ else:
150
+ line = f" {prop_name}: {py_type}"
151
+ if len(line) > 119:
152
+ # Break long single-line annotations
153
+ lines.append(f" {prop_name}: (")
154
+ lines.append(f" {py_type}")
155
+ lines.append(" )")
156
+ else:
157
+ lines.append(line)
158
+
159
+ return "\n".join(lines) + "\n"
160
+
161
+
162
+ def generate_all(discovery: dict[str, Any], version: str) -> str:
163
+ """Generate the full _generated_types.py file content."""
164
+ schemas = discovery.get("schemas", {})
165
+
166
+ if version == "v1":
167
+ version_prefix = "GoogleCloudAiplatformV1"
168
+ else:
169
+ version_prefix = "GoogleCloudAiplatformV1beta1"
170
+
171
+ header = f'''\
172
+ """TypedDict definitions for Google Vertex AI generateContent API.
173
+
174
+ Auto-generated from the Vertex AI discovery document ({version}).
175
+ Do not edit manually — regenerate with:
176
+ python scripts/generate_types.py --version {version}
177
+
178
+ API reference (discovery docs):
179
+ https://aiplatform.googleapis.com/$discovery/rest?version=v1
180
+ https://aiplatform.googleapis.com/$discovery/rest?version=v1beta1
181
+ """
182
+
183
+ from __future__ import annotations
184
+
185
+ from typing import Any, Literal, TypedDict # noqa: UP035
186
+
187
+ '''
188
+
189
+ parts: list[str] = [header]
190
+
191
+ # Track which schemas we actually found
192
+ generated: list[str] = []
193
+ missing: list[str] = []
194
+
195
+ for td_name, schema_suffix in SCHEMA_MAP.items():
196
+ full_id = f"{version_prefix}{schema_suffix}"
197
+ schema = schemas.get(full_id)
198
+ if schema is None:
199
+ missing.append(f"# WARNING: schema {full_id!r} not found in discovery doc")
200
+ continue
201
+ parts.append(generate_typeddict(td_name, schema, version_prefix))
202
+ parts.append("")
203
+ generated.append(td_name)
204
+
205
+ # __all__ for explicit exports
206
+ all_items = [f' "{n}",' for n in generated]
207
+ all_block = "__all__ = [\n" + "\n".join(all_items) + "\n]\n"
208
+ parts.insert(1, all_block)
209
+ parts.insert(2, "")
210
+
211
+ if missing:
212
+ parts.append("\n".join(missing) + "\n")
213
+
214
+ for m in missing:
215
+ print(m, file=sys.stderr)
216
+
217
+ return "\n".join(parts)
218
+
219
+
220
+ def main() -> None:
221
+ parser = argparse.ArgumentParser(description=__doc__)
222
+ parser.add_argument(
223
+ "--version",
224
+ default="v1beta1",
225
+ choices=["v1", "v1beta1"],
226
+ help="Discovery document version (default: v1beta1)",
227
+ )
228
+ parser.add_argument(
229
+ "--output",
230
+ default="src/axio_transport_google/_generated_types.py",
231
+ help="Output file path",
232
+ )
233
+ parser.add_argument(
234
+ "--input",
235
+ default=None,
236
+ help="Read discovery doc from file instead of fetching",
237
+ )
238
+ args = parser.parse_args()
239
+
240
+ if args.input:
241
+ with open(args.input) as f:
242
+ discovery = json.load(f)
243
+ else:
244
+ discovery = fetch_discovery(args.version)
245
+
246
+ content = generate_all(discovery, args.version)
247
+
248
+ with open(args.output, "w") as f:
249
+ f.write(content)
250
+ print(f"Generated {args.output} ({len(SCHEMA_MAP)} schemas)", file=sys.stderr)
251
+
252
+
253
+ if __name__ == "__main__":
254
+ main()