geomind-ai 1.0.3__py3-none-any.whl → 1.0.4__py3-none-any.whl
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.
- geomind/__init__.py +1 -1
- geomind/agent.py +65 -39
- geomind/config.py +5 -2
- {geomind_ai-1.0.3.dist-info → geomind_ai-1.0.4.dist-info}/METADATA +1 -1
- geomind_ai-1.0.4.dist-info/RECORD +14 -0
- geomind_ai-1.0.3.dist-info/RECORD +0 -14
- {geomind_ai-1.0.3.dist-info → geomind_ai-1.0.4.dist-info}/WHEEL +0 -0
- {geomind_ai-1.0.3.dist-info → geomind_ai-1.0.4.dist-info}/entry_points.txt +0 -0
- {geomind_ai-1.0.3.dist-info → geomind_ai-1.0.4.dist-info}/licenses/LICENSE +0 -0
- {geomind_ai-1.0.3.dist-info → geomind_ai-1.0.4.dist-info}/top_level.txt +0 -0
geomind/__init__.py
CHANGED
geomind/agent.py
CHANGED
|
@@ -12,7 +12,14 @@ from datetime import datetime
|
|
|
12
12
|
|
|
13
13
|
from openai import OpenAI
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
import requests
|
|
16
|
+
|
|
17
|
+
from .config import (
|
|
18
|
+
OPENROUTER_API_KEY,
|
|
19
|
+
OPENROUTER_API_URL,
|
|
20
|
+
OPENROUTER_MODEL,
|
|
21
|
+
GEOMIND_PROXY_URL,
|
|
22
|
+
)
|
|
16
23
|
from .tools import (
|
|
17
24
|
geocode_location,
|
|
18
25
|
get_bbox_from_location,
|
|
@@ -242,28 +249,25 @@ class GeoMindAgent:
|
|
|
242
249
|
|
|
243
250
|
Args:
|
|
244
251
|
model: Model name (default: xiaomi/mimo-v2-flash:free)
|
|
245
|
-
api_key: OpenRouter API key. If not provided,
|
|
252
|
+
api_key: OpenRouter API key. If not provided, uses free proxy.
|
|
246
253
|
"""
|
|
247
254
|
self.provider = "openrouter"
|
|
248
255
|
self.api_key = api_key or OPENROUTER_API_KEY
|
|
249
256
|
self.model_name = model or OPENROUTER_MODEL
|
|
250
|
-
self.
|
|
251
|
-
|
|
252
|
-
if
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
# Create OpenAI-compatible client
|
|
266
|
-
self.client = OpenAI(base_url=self.base_url, api_key=self.api_key)
|
|
257
|
+
self.use_proxy = not self.api_key # Use proxy if no API key provided
|
|
258
|
+
|
|
259
|
+
if self.use_proxy:
|
|
260
|
+
self.base_url = GEOMIND_PROXY_URL
|
|
261
|
+
print(f"🚀 GeoMind Agent initialized with {self.model_name} (via proxy)")
|
|
262
|
+
print(f" Proxy URL: {self.base_url}")
|
|
263
|
+
# No OpenAI client needed for proxy
|
|
264
|
+
self.client = None
|
|
265
|
+
else:
|
|
266
|
+
self.base_url = OPENROUTER_API_URL
|
|
267
|
+
print(f"🚀 GeoMind Agent initialized with {self.model_name} (OpenRouter)")
|
|
268
|
+
print(f" API URL: {self.base_url}")
|
|
269
|
+
# Create OpenAI-compatible client
|
|
270
|
+
self.client = OpenAI(base_url=self.base_url, api_key=self.api_key)
|
|
267
271
|
|
|
268
272
|
# Chat history
|
|
269
273
|
self.history = []
|
|
@@ -296,6 +300,31 @@ When users ask for imagery:
|
|
|
296
300
|
|
|
297
301
|
Always explain what you're doing and interpret results in a helpful way."""
|
|
298
302
|
|
|
303
|
+
def _call_llm(self, messages: list, tools: list) -> dict:
|
|
304
|
+
"""Call LLM via proxy or direct OpenAI client."""
|
|
305
|
+
if self.use_proxy:
|
|
306
|
+
# Use proxy endpoint
|
|
307
|
+
payload = {
|
|
308
|
+
"model": self.model_name,
|
|
309
|
+
"messages": messages,
|
|
310
|
+
"tools": tools,
|
|
311
|
+
"tool_choice": "auto",
|
|
312
|
+
"max_tokens": 4096,
|
|
313
|
+
}
|
|
314
|
+
response = requests.post(self.base_url, json=payload, timeout=120)
|
|
315
|
+
response.raise_for_status()
|
|
316
|
+
return response.json()
|
|
317
|
+
else:
|
|
318
|
+
# Use OpenAI client directly
|
|
319
|
+
response = self.client.chat.completions.create(
|
|
320
|
+
model=self.model_name,
|
|
321
|
+
messages=messages,
|
|
322
|
+
tools=tools,
|
|
323
|
+
tool_choice="auto",
|
|
324
|
+
max_tokens=4096,
|
|
325
|
+
)
|
|
326
|
+
return response.model_dump()
|
|
327
|
+
|
|
299
328
|
def _execute_function(self, name: str, args: dict) -> dict:
|
|
300
329
|
"""Execute a function call and return the result."""
|
|
301
330
|
print(f" 🔧 Executing: {name}({args})")
|
|
@@ -329,42 +358,39 @@ Always explain what you're doing and interpret results in a helpful way."""
|
|
|
329
358
|
while iteration < max_iterations:
|
|
330
359
|
iteration += 1
|
|
331
360
|
|
|
332
|
-
# Call the model
|
|
333
|
-
|
|
334
|
-
model=self.model_name,
|
|
335
|
-
messages=messages,
|
|
336
|
-
tools=TOOLS,
|
|
337
|
-
tool_choice="auto",
|
|
338
|
-
max_tokens=4096,
|
|
339
|
-
)
|
|
361
|
+
# Call the model (via proxy or direct)
|
|
362
|
+
response_data = self._call_llm(messages, TOOLS)
|
|
340
363
|
|
|
341
|
-
|
|
364
|
+
# Extract assistant message from response
|
|
365
|
+
choice = response_data["choices"][0]
|
|
366
|
+
assistant_message = choice["message"]
|
|
342
367
|
|
|
343
368
|
# Check if there are tool calls
|
|
344
|
-
|
|
369
|
+
tool_calls = assistant_message.get("tool_calls", [])
|
|
370
|
+
if tool_calls:
|
|
345
371
|
# Add assistant message with tool calls to messages
|
|
346
372
|
messages.append(
|
|
347
373
|
{
|
|
348
374
|
"role": "assistant",
|
|
349
|
-
"content": assistant_message.content or "",
|
|
375
|
+
"content": assistant_message.get("content") or "",
|
|
350
376
|
"tool_calls": [
|
|
351
377
|
{
|
|
352
|
-
"id": tc
|
|
378
|
+
"id": tc["id"],
|
|
353
379
|
"type": "function",
|
|
354
380
|
"function": {
|
|
355
|
-
"name": tc
|
|
356
|
-
"arguments": tc
|
|
381
|
+
"name": tc["function"]["name"],
|
|
382
|
+
"arguments": tc["function"]["arguments"],
|
|
357
383
|
},
|
|
358
384
|
}
|
|
359
|
-
for tc in
|
|
385
|
+
for tc in tool_calls
|
|
360
386
|
],
|
|
361
387
|
}
|
|
362
388
|
)
|
|
363
389
|
|
|
364
390
|
# Execute each tool call
|
|
365
|
-
for tool_call in
|
|
366
|
-
func_name = tool_call
|
|
367
|
-
func_args = json.loads(tool_call
|
|
391
|
+
for tool_call in tool_calls:
|
|
392
|
+
func_name = tool_call["function"]["name"]
|
|
393
|
+
func_args = json.loads(tool_call["function"]["arguments"])
|
|
368
394
|
|
|
369
395
|
result = self._execute_function(func_name, func_args)
|
|
370
396
|
|
|
@@ -372,13 +398,13 @@ Always explain what you're doing and interpret results in a helpful way."""
|
|
|
372
398
|
messages.append(
|
|
373
399
|
{
|
|
374
400
|
"role": "tool",
|
|
375
|
-
"tool_call_id": tool_call
|
|
401
|
+
"tool_call_id": tool_call["id"],
|
|
376
402
|
"content": json.dumps(result, default=str),
|
|
377
403
|
}
|
|
378
404
|
)
|
|
379
405
|
else:
|
|
380
406
|
# No tool calls, we have a final response
|
|
381
|
-
final_text = assistant_message.content or ""
|
|
407
|
+
final_text = assistant_message.get("content") or ""
|
|
382
408
|
|
|
383
409
|
# Add to history
|
|
384
410
|
self.history.append({"role": "assistant", "content": final_text})
|
geomind/config.py
CHANGED
|
@@ -49,7 +49,10 @@ OUTPUT_DIR.mkdir(exist_ok=True)
|
|
|
49
49
|
GEOCODER_USER_AGENT = "geomind_agent_v0.1"
|
|
50
50
|
|
|
51
51
|
# OpenRouter API Configuration
|
|
52
|
-
#
|
|
53
|
-
|
|
52
|
+
# Uses Cloudflare Workers proxy to securely handle API key
|
|
53
|
+
GEOMIND_PROXY_URL = "https://geomind-proxy.harshinde.workers.dev"
|
|
54
|
+
|
|
55
|
+
# Users can optionally provide their own OpenRouter key to bypass proxy
|
|
56
|
+
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "")
|
|
54
57
|
OPENROUTER_API_URL = os.getenv("OPENROUTER_API_URL", "https://openrouter.ai/api/v1")
|
|
55
58
|
OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL", "xiaomi/mimo-v2-flash:free")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
geomind/__init__.py,sha256=l1saSR9mdB8meBsMNkp1uCJTh4y4lCsAU-iunasV6bc,151
|
|
2
|
+
geomind/agent.py,sha256=KJvgEulr9zOttbTz_paZD8uDrdgDlbr9UnaqmKgdXa4,16238
|
|
3
|
+
geomind/cli.py,sha256=tmMkvO9scyvzpRtiKbcb1jtWn3fSgFoywQmuusmjDuI,3283
|
|
4
|
+
geomind/config.py,sha256=hgYQ33ZL9xeHzZXBFLEtx4tJX7ZHN14Ft0_ru7sHMQQ,2154
|
|
5
|
+
geomind/tools/__init__.py,sha256=8iumGwIFHh8Bj1VJNgZtmKnEBqCy6_cRkzYENDUH7x4,720
|
|
6
|
+
geomind/tools/geocoding.py,sha256=hiLpzHpkJP6IgWAUtZMnHL6qpkWcYWVLpGe0yfYxXv8,3007
|
|
7
|
+
geomind/tools/processing.py,sha256=vMp8PMb8h8QiBRBFRvI_TGRqEDBTDQvSV0zvC1Ji5bc,9976
|
|
8
|
+
geomind/tools/stac_search.py,sha256=V6230l4aHjedPWXu-3Cjmfc6diSFh5zsycewUko0W8k,6452
|
|
9
|
+
geomind_ai-1.0.4.dist-info/licenses/LICENSE,sha256=aveu0ERm7I3NnIu8rtpKdvd0eyRpmktXKU0PBABtSN0,1069
|
|
10
|
+
geomind_ai-1.0.4.dist-info/METADATA,sha256=Mf_JOGNdILojqcMYcxjlb-uqSnj42iISXsCCidylcv0,2233
|
|
11
|
+
geomind_ai-1.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
geomind_ai-1.0.4.dist-info/entry_points.txt,sha256=2nPR3faYKl0-1epccvzMJ2xdi-Q1Vt7aOSvA84oIWnw,45
|
|
13
|
+
geomind_ai-1.0.4.dist-info/top_level.txt,sha256=rjKWNSNRhq4R9xJoZGsG-eAaH7BmTVNvfrrbcaJMIIs,8
|
|
14
|
+
geomind_ai-1.0.4.dist-info/RECORD,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
geomind/__init__.py,sha256=n5p83zzIUSJlgGvqvyilFeU5u84if0DoTwsdj4gcLNE,151
|
|
2
|
-
geomind/agent.py,sha256=t5xQ5oldG5BrZfuk67RSKuDNl1TmIxwvPqbkvJqO_hE,15398
|
|
3
|
-
geomind/cli.py,sha256=tmMkvO9scyvzpRtiKbcb1jtWn3fSgFoywQmuusmjDuI,3283
|
|
4
|
-
geomind/config.py,sha256=hv3DNM7QbCuOSWuPmXeiVxEbGxH7fRLxOc8jexd7rrY,2011
|
|
5
|
-
geomind/tools/__init__.py,sha256=8iumGwIFHh8Bj1VJNgZtmKnEBqCy6_cRkzYENDUH7x4,720
|
|
6
|
-
geomind/tools/geocoding.py,sha256=hiLpzHpkJP6IgWAUtZMnHL6qpkWcYWVLpGe0yfYxXv8,3007
|
|
7
|
-
geomind/tools/processing.py,sha256=vMp8PMb8h8QiBRBFRvI_TGRqEDBTDQvSV0zvC1Ji5bc,9976
|
|
8
|
-
geomind/tools/stac_search.py,sha256=V6230l4aHjedPWXu-3Cjmfc6diSFh5zsycewUko0W8k,6452
|
|
9
|
-
geomind_ai-1.0.3.dist-info/licenses/LICENSE,sha256=aveu0ERm7I3NnIu8rtpKdvd0eyRpmktXKU0PBABtSN0,1069
|
|
10
|
-
geomind_ai-1.0.3.dist-info/METADATA,sha256=y6efOTHaisR2kWz4J4BpQLc6rLV7iMyTvKkuK2yuYM8,2233
|
|
11
|
-
geomind_ai-1.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
-
geomind_ai-1.0.3.dist-info/entry_points.txt,sha256=2nPR3faYKl0-1epccvzMJ2xdi-Q1Vt7aOSvA84oIWnw,45
|
|
13
|
-
geomind_ai-1.0.3.dist-info/top_level.txt,sha256=rjKWNSNRhq4R9xJoZGsG-eAaH7BmTVNvfrrbcaJMIIs,8
|
|
14
|
-
geomind_ai-1.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|