gemixy 1.0.0__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.
gemixy-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pooraddyy
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 all
13
+ 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 THE
21
+ SOFTWARE.
gemixy-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: gemixy
3
+ Version: 1.0.0
4
+ Summary: Python SDK for Gemini OpenAI Proxy
5
+ Author: pooraddyy
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.8
11
+ License-File: LICENSE
12
+ Requires-Dist: requests>=2.28.0
13
+ Dynamic: author
14
+ Dynamic: classifier
15
+ Dynamic: license
16
+ Dynamic: license-file
17
+ Dynamic: requires-dist
18
+ Dynamic: requires-python
19
+ Dynamic: summary
gemixy-1.0.0/README.md ADDED
@@ -0,0 +1,172 @@
1
+ <div align="center">
2
+
3
+ # gemixy
4
+
5
+ ### Python SDK for Google Gemini - No API Key Needed
6
+
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
+ [![Python 3.8+](https://img.shields.io/badge/Python-3.8+-3776AB.svg)](https://www.python.org/)
9
+ [![PyPI](https://img.shields.io/badge/PyPI-gemixy-red.svg)](https://pypi.org/project/gemixy/)
10
+
11
+ ---
12
+
13
+ Direct Gemini access via web scraping. No API key, no server, no setup.
14
+
15
+ [Installation](#installation) | [Quick Start](#quick-start) | [Deploy Server](#deploy-server) | [Docs](DOCS.md)
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pip install gemixy
25
+ ```
26
+
27
+ ---
28
+
29
+ ## Quick Start
30
+
31
+ ```python
32
+ from gemixy import GeminiClient
33
+
34
+ client = GeminiClient()
35
+ print(client.chat("Hello!"))
36
+ ```
37
+
38
+ That's it. No API key. No server. No configuration.
39
+
40
+ ---
41
+
42
+ ## Features
43
+
44
+ - **Zero config** - just `pip install gemixy` and use
45
+ - **No API key** - uses Gemini web scraping
46
+ - **No server needed** - works directly in Python
47
+ - **OpenAI compatible** - drop-in replacement
48
+ - **Streaming support** - real-time responses
49
+
50
+ ---
51
+
52
+ ## SDK Usage
53
+
54
+ ### Simple Chat
55
+
56
+ ```python
57
+ from gemixy import GeminiClient
58
+
59
+ client = GeminiClient()
60
+ response = client.chat("What is AI?")
61
+ print(response)
62
+ ```
63
+
64
+ ### Streaming
65
+
66
+ ```python
67
+ from gemixy import GeminiClient
68
+
69
+ client = GeminiClient()
70
+ for chunk in client.chat_stream("Tell me a story"):
71
+ print(chunk, end="", flush=True)
72
+ ```
73
+
74
+ ### Message History
75
+
76
+ ```python
77
+ from gemixy import GeminiClient
78
+
79
+ client = GeminiClient()
80
+ messages = [
81
+ {"role": "system", "content": "You are a helpful assistant"},
82
+ {"role": "user", "content": "What is Python?"}
83
+ ]
84
+ print(client.messages(messages))
85
+ ```
86
+
87
+ ### List Models
88
+
89
+ ```python
90
+ from gemixy import GeminiClient
91
+
92
+ client = GeminiClient()
93
+ print(client.models())
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Deploy Server
99
+
100
+ Optional - if you want an OpenAI-compatible API server:
101
+
102
+ ### Vercel
103
+
104
+ ```bash
105
+ vercel --yes
106
+ ```
107
+
108
+ [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/pooraddyy/gemini-openai-proxy-python)
109
+
110
+ ### Docker
111
+
112
+ ```bash
113
+ docker build -t gemixy .
114
+ docker run -d -p 5000:5000 gemixy
115
+ ```
116
+
117
+ ### Local
118
+
119
+ ```bash
120
+ git clone https://github.com/pooraddyy/gemini-openai-proxy-python.git
121
+ cd gemini-openai-proxy-python
122
+ pip install -r requirements.txt
123
+ python run.py
124
+ ```
125
+
126
+ Server starts at `http://localhost:5000`
127
+
128
+ ---
129
+
130
+ ## API Server Endpoints
131
+
132
+ | Method | Endpoint | Description |
133
+ |--------|----------|-------------|
134
+ | GET | `/v1/models` | List models |
135
+ | POST | `/v1/chat/completions` | Chat completions |
136
+
137
+ ### cURL Examples
138
+
139
+ ```bash
140
+ curl http://localhost:5000/v1/models
141
+
142
+ curl http://localhost:5000/v1/chat/completions \
143
+ -H "Content-Type: application/json" \
144
+ -d '{"model": "gemini", "messages": [{"role": "user", "content": "Hello!"}]}'
145
+ ```
146
+
147
+ ---
148
+
149
+ ## OpenAI Client Library
150
+
151
+ Works with official OpenAI library (requires server):
152
+
153
+ ```python
154
+ from openai import OpenAI
155
+
156
+ client = OpenAI(
157
+ base_url="http://localhost:5000/v1",
158
+ api_key="any-key"
159
+ )
160
+
161
+ response = client.chat.completions.create(
162
+ model="gemini",
163
+ messages=[{"role": "user", "content": "Hello!"}]
164
+ )
165
+ print(response.choices[0].message.content)
166
+ ```
167
+
168
+ ---
169
+
170
+ ## License
171
+
172
+ [MIT](LICENSE) - pooraddyy
@@ -0,0 +1,15 @@
1
+ from .core import (
2
+ chat_with_gemini,
3
+ messages_to_prompt,
4
+ ok_response,
5
+ chunk_response,
6
+ chunk_done,
7
+ )
8
+
9
+ __all__ = [
10
+ "chat_with_gemini",
11
+ "messages_to_prompt",
12
+ "ok_response",
13
+ "chunk_response",
14
+ "chunk_done",
15
+ ]
@@ -0,0 +1,162 @@
1
+ import json
2
+ import re
3
+ import uuid
4
+ import time
5
+ import requests
6
+
7
+ def extract_snlm0e_token(html):
8
+ for pattern in [r'"SNlM0e":"([^"]+)"', r'"FdrFJe":"([^"]+)"', r'"cfb2h":"([^"]+)"']:
9
+ match = re.search(pattern, html)
10
+ if match and len(match.group(1)) > 20:
11
+ return match.group(1)
12
+ return None
13
+
14
+ def extract_build_params(html):
15
+ params = {}
16
+ bl = re.search(r'boq[_-]assistant[^"\']*_(\d+\.\d+[^"\']*)', html)
17
+ if bl:
18
+ params['bl'] = f'boq_assistant-bard-web-server_{bl.group(1)}'
19
+ fsid = re.search(r'f\.sid["\']?\s*[:=]\s*["\']?([^"\'&\s]+)', html)
20
+ if fsid:
21
+ params['fsid'] = fsid.group(1)
22
+ reqid = re.search(r'_reqid["\']?\s*[:=]\s*["\']?(\d+)', html)
23
+ if reqid:
24
+ params['reqid'] = int(reqid.group(1))
25
+ params.setdefault('bl', 'boq_assistant-bard-web-server_20251217.07_p5')
26
+ params.setdefault('fsid', str(-1 * int(time.time() * 1000)))
27
+ params.setdefault('reqid', int(time.time() * 1000) % 1000000)
28
+ return params
29
+
30
+ def get_session():
31
+ session = requests.Session()
32
+ headers = {
33
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
34
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
35
+ 'Accept-Language': 'en-US,en;q=0.9',
36
+ }
37
+ resp = session.get('https://gemini.google.com/app', headers=headers, timeout=30)
38
+ snlm0e = extract_snlm0e_token(resp.text)
39
+ if not snlm0e:
40
+ return None
41
+ params = extract_build_params(resp.text)
42
+ return {'session': session, 'snlm0e': snlm0e, **params}
43
+
44
+ def build_payload(prompt, snlm0e):
45
+ escaped = prompt.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n')
46
+ rid = str(uuid.uuid4()).upper()
47
+ payload = [
48
+ [escaped, 0, None, None, None, None, 0],
49
+ ["en-US"],
50
+ ["", "", "", None, None, None, None, None, None, ""],
51
+ snlm0e, uuid.uuid4().hex, None, [0], 1,
52
+ None, None, 1, 0, None, None, None, None, None,
53
+ [[0]], 0, None, None, None, None, None, None, None, None, 1, None, None,
54
+ [4], None, None, None, None, None, None, None, None, None, None,
55
+ [2], None, None, None, None, None, None, None, None, None, None, None,
56
+ 0, None, None, None, None, None, rid, None, []
57
+ ]
58
+ p = json.dumps(payload, separators=(',', ':')).replace('\\', '\\\\').replace('"', '\\"')
59
+ return {'f.req': f'[null,"{p}"]', '': ''}
60
+
61
+ def parse_response(text):
62
+ full = ""
63
+ for line in text.strip().split('\n'):
64
+ if not line or line.startswith(')]}'):
65
+ continue
66
+ try:
67
+ data = json.loads(line)
68
+ if isinstance(data, list) and data[0][0] == "wrb.fr" and len(data[0]) > 2:
69
+ inner = data[0][2]
70
+ if inner:
71
+ parsed = json.loads(inner)
72
+ if isinstance(parsed, list) and len(parsed) > 4:
73
+ content = parsed[4]
74
+ if isinstance(content, list) and len(content) > 0:
75
+ item = content[0]
76
+ if isinstance(item, list) and len(item) > 1:
77
+ arr = item[1]
78
+ if isinstance(arr, list) and len(arr) > 0 and isinstance(arr[0], str):
79
+ if len(arr[0]) > len(full):
80
+ full = arr[0]
81
+ except Exception:
82
+ continue
83
+ return full.replace('\\n', '\n').replace('\\"', '"').replace('\\\\', '\\') if full else None
84
+
85
+ def chat_with_gemini(prompt):
86
+ data = get_session()
87
+ if not data:
88
+ return None
89
+ sess = data['session']
90
+ url = (f"https://gemini.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate"
91
+ f"?bl={data['bl']}&f.sid={data['fsid']}&hl=en-US&_reqid={data['reqid']}&rt=c")
92
+ cookie = '; '.join(f"{k}={v}" for k, v in sess.cookies.items())
93
+ headers = {
94
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
95
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
96
+ 'x-same-domain': '1',
97
+ 'origin': 'https://gemini.google.com',
98
+ 'referer': 'https://gemini.google.com/',
99
+ 'Cookie': cookie,
100
+ }
101
+ resp = sess.post(url, data=build_payload(prompt, data['snlm0e']), headers=headers, timeout=60)
102
+ if resp.status_code != 200:
103
+ return None
104
+ return parse_response(resp.text)
105
+
106
+ def messages_to_prompt(messages):
107
+ parts = []
108
+ for m in messages:
109
+ role = m.get("role", "user")
110
+ content = m.get("content", "")
111
+ if isinstance(content, str):
112
+ if role == "system":
113
+ parts.append(f"[System]: {content}")
114
+ elif role == "assistant":
115
+ parts.append(f"[Assistant]: {content}")
116
+ else:
117
+ parts.append(content)
118
+ elif isinstance(content, list):
119
+ for item in content:
120
+ if isinstance(item, dict) and item.get("type") == "text":
121
+ parts.append(item.get("text", ""))
122
+ return "\n".join(parts)
123
+
124
+ def ok_response(text, model, req_id=None):
125
+ return {
126
+ "id": req_id or f"chatcmpl-{uuid.uuid4().hex}",
127
+ "object": "chat.completion",
128
+ "created": int(time.time()),
129
+ "model": model,
130
+ "choices": [{
131
+ "index": 0,
132
+ "message": {"role": "assistant", "content": text},
133
+ "finish_reason": "stop"
134
+ }],
135
+ "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}
136
+ }
137
+
138
+ def chunk_response(text, resp_id, created, model):
139
+ return {
140
+ "id": resp_id,
141
+ "object": "chat.completion.chunk",
142
+ "created": created,
143
+ "model": model,
144
+ "choices": [{
145
+ "index": 0,
146
+ "delta": {"content": text},
147
+ "finish_reason": None
148
+ }]
149
+ }
150
+
151
+ def chunk_done(resp_id, created, model):
152
+ return {
153
+ "id": resp_id,
154
+ "object": "chat.completion.chunk",
155
+ "created": created,
156
+ "model": model,
157
+ "choices": [{
158
+ "index": 0,
159
+ "delta": {},
160
+ "finish_reason": "stop"
161
+ }]
162
+ }
@@ -0,0 +1,57 @@
1
+ import os
2
+ import sys
3
+ import json
4
+ from typing import List, Iterator
5
+
6
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
7
+ from gemini.core import chat_with_gemini, messages_to_prompt
8
+
9
+
10
+ class GeminiClient:
11
+ def __init__(self, base_url: str = None, api_key: str = "any"):
12
+ self.base_url = base_url
13
+ self.api_key = api_key
14
+
15
+ def chat(self, message: str, model: str = "gemini", **kwargs) -> str:
16
+ result = chat_with_gemini(message)
17
+ if not result:
18
+ raise RuntimeError("Gemini did not respond")
19
+ return result
20
+
21
+ def chat_stream(self, message: str, model: str = "gemini", **kwargs) -> Iterator[str]:
22
+ result = chat_with_gemini(message)
23
+ if not result:
24
+ raise RuntimeError("Gemini did not respond")
25
+ for word in result.split():
26
+ yield word + " "
27
+
28
+ def messages(self, messages: List[dict], model: str = "gemini", **kwargs) -> str:
29
+ prompt = messages_to_prompt(messages)
30
+ result = chat_with_gemini(prompt)
31
+ if not result:
32
+ raise RuntimeError("Gemini did not respond")
33
+ return result
34
+
35
+ def messages_stream(self, messages: List[dict], model: str = "gemini", **kwargs) -> Iterator[str]:
36
+ prompt = messages_to_prompt(messages)
37
+ result = chat_with_gemini(prompt)
38
+ if not result:
39
+ raise RuntimeError("Gemini did not respond")
40
+ for word in result.split():
41
+ yield word + " "
42
+
43
+ def models(self) -> dict:
44
+ return {
45
+ "object": "list",
46
+ "data": [
47
+ {
48
+ "id": "gemini",
49
+ "object": "model",
50
+ "created": 1686935002,
51
+ "owned_by": "google"
52
+ }
53
+ ]
54
+ }
55
+
56
+
57
+ client = GeminiClient()
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: gemixy
3
+ Version: 1.0.0
4
+ Summary: Python SDK for Gemini OpenAI Proxy
5
+ Author: pooraddyy
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.8
11
+ License-File: LICENSE
12
+ Requires-Dist: requests>=2.28.0
13
+ Dynamic: author
14
+ Dynamic: classifier
15
+ Dynamic: license
16
+ Dynamic: license-file
17
+ Dynamic: requires-dist
18
+ Dynamic: requires-python
19
+ Dynamic: summary
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ gemini/__init__.py
5
+ gemini/core.py
6
+ gemixy/__init__.py
7
+ gemixy.egg-info/PKG-INFO
8
+ gemixy.egg-info/SOURCES.txt
9
+ gemixy.egg-info/dependency_links.txt
10
+ gemixy.egg-info/requires.txt
11
+ gemixy.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ requests>=2.28.0
@@ -0,0 +1,2 @@
1
+ gemini
2
+ gemixy
gemixy-1.0.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
gemixy-1.0.0/setup.py ADDED
@@ -0,0 +1,19 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="gemixy",
5
+ version="1.0.0",
6
+ description="Python SDK for Gemini OpenAI Proxy",
7
+ author="pooraddyy",
8
+ license="MIT",
9
+ packages=find_packages(),
10
+ python_requires=">=3.8",
11
+ install_requires=[
12
+ "requests>=2.28.0",
13
+ ],
14
+ classifiers=[
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ],
19
+ )