geomind-ai 1.0.2__tar.gz → 1.0.4__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.
- {geomind_ai-1.0.2/geomind_ai.egg-info → geomind_ai-1.0.4}/PKG-INFO +1 -1
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind/__init__.py +1 -1
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind/agent.py +67 -36
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind/config.py +5 -2
- {geomind_ai-1.0.2 → geomind_ai-1.0.4/geomind_ai.egg-info}/PKG-INFO +1 -1
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/pyproject.toml +1 -1
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/LICENSE +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/MANIFEST.in +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/README.md +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind/cli.py +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind/tools/__init__.py +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind/tools/geocoding.py +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind/tools/processing.py +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind/tools/stac_search.py +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind_ai.egg-info/SOURCES.txt +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind_ai.egg-info/dependency_links.txt +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind_ai.egg-info/entry_points.txt +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind_ai.egg-info/requires.txt +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/geomind_ai.egg-info/top_level.txt +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/requirements.txt +0 -0
- {geomind_ai-1.0.2 → geomind_ai-1.0.4}/setup.cfg +0 -0
|
@@ -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,
|
|
@@ -236,29 +243,31 @@ class GeoMindAgent:
|
|
|
236
243
|
Uses OpenRouter API for access to multiple AI models.
|
|
237
244
|
"""
|
|
238
245
|
|
|
239
|
-
def __init__(self, model: Optional[str] = None):
|
|
246
|
+
def __init__(self, model: Optional[str] = None, api_key: Optional[str] = None):
|
|
240
247
|
"""
|
|
241
248
|
Initialize the GeoMind agent.
|
|
242
249
|
|
|
243
250
|
Args:
|
|
244
251
|
model: Model name (default: xiaomi/mimo-v2-flash:free)
|
|
252
|
+
api_key: OpenRouter API key. If not provided, uses free proxy.
|
|
245
253
|
"""
|
|
246
254
|
self.provider = "openrouter"
|
|
247
|
-
self.api_key = OPENROUTER_API_KEY
|
|
255
|
+
self.api_key = api_key or OPENROUTER_API_KEY
|
|
248
256
|
self.model_name = model or OPENROUTER_MODEL
|
|
249
|
-
self.
|
|
250
|
-
|
|
251
|
-
if
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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)
|
|
262
271
|
|
|
263
272
|
# Chat history
|
|
264
273
|
self.history = []
|
|
@@ -291,6 +300,31 @@ When users ask for imagery:
|
|
|
291
300
|
|
|
292
301
|
Always explain what you're doing and interpret results in a helpful way."""
|
|
293
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
|
+
|
|
294
328
|
def _execute_function(self, name: str, args: dict) -> dict:
|
|
295
329
|
"""Execute a function call and return the result."""
|
|
296
330
|
print(f" 🔧 Executing: {name}({args})")
|
|
@@ -324,42 +358,39 @@ Always explain what you're doing and interpret results in a helpful way."""
|
|
|
324
358
|
while iteration < max_iterations:
|
|
325
359
|
iteration += 1
|
|
326
360
|
|
|
327
|
-
# Call the model
|
|
328
|
-
|
|
329
|
-
model=self.model_name,
|
|
330
|
-
messages=messages,
|
|
331
|
-
tools=TOOLS,
|
|
332
|
-
tool_choice="auto",
|
|
333
|
-
max_tokens=4096,
|
|
334
|
-
)
|
|
361
|
+
# Call the model (via proxy or direct)
|
|
362
|
+
response_data = self._call_llm(messages, TOOLS)
|
|
335
363
|
|
|
336
|
-
|
|
364
|
+
# Extract assistant message from response
|
|
365
|
+
choice = response_data["choices"][0]
|
|
366
|
+
assistant_message = choice["message"]
|
|
337
367
|
|
|
338
368
|
# Check if there are tool calls
|
|
339
|
-
|
|
369
|
+
tool_calls = assistant_message.get("tool_calls", [])
|
|
370
|
+
if tool_calls:
|
|
340
371
|
# Add assistant message with tool calls to messages
|
|
341
372
|
messages.append(
|
|
342
373
|
{
|
|
343
374
|
"role": "assistant",
|
|
344
|
-
"content": assistant_message.content or "",
|
|
375
|
+
"content": assistant_message.get("content") or "",
|
|
345
376
|
"tool_calls": [
|
|
346
377
|
{
|
|
347
|
-
"id": tc
|
|
378
|
+
"id": tc["id"],
|
|
348
379
|
"type": "function",
|
|
349
380
|
"function": {
|
|
350
|
-
"name": tc
|
|
351
|
-
"arguments": tc
|
|
381
|
+
"name": tc["function"]["name"],
|
|
382
|
+
"arguments": tc["function"]["arguments"],
|
|
352
383
|
},
|
|
353
384
|
}
|
|
354
|
-
for tc in
|
|
385
|
+
for tc in tool_calls
|
|
355
386
|
],
|
|
356
387
|
}
|
|
357
388
|
)
|
|
358
389
|
|
|
359
390
|
# Execute each tool call
|
|
360
|
-
for tool_call in
|
|
361
|
-
func_name = tool_call
|
|
362
|
-
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"])
|
|
363
394
|
|
|
364
395
|
result = self._execute_function(func_name, func_args)
|
|
365
396
|
|
|
@@ -367,13 +398,13 @@ Always explain what you're doing and interpret results in a helpful way."""
|
|
|
367
398
|
messages.append(
|
|
368
399
|
{
|
|
369
400
|
"role": "tool",
|
|
370
|
-
"tool_call_id": tool_call
|
|
401
|
+
"tool_call_id": tool_call["id"],
|
|
371
402
|
"content": json.dumps(result, default=str),
|
|
372
403
|
}
|
|
373
404
|
)
|
|
374
405
|
else:
|
|
375
406
|
# No tool calls, we have a final response
|
|
376
|
-
final_text = assistant_message.content or ""
|
|
407
|
+
final_text = assistant_message.get("content") or ""
|
|
377
408
|
|
|
378
409
|
# Add to history
|
|
379
410
|
self.history.append({"role": "assistant", "content": final_text})
|
|
@@ -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")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|