geomind-ai 1.0.3__tar.gz → 1.0.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geomind-ai
3
- Version: 1.0.3
3
+ Version: 1.0.5
4
4
  Summary: AI agent for geospatial analysis with Sentinel-2 satellite imagery
5
5
  Author: Harsh Shinde
6
6
  License-Expression: MIT
@@ -3,7 +3,7 @@ GeoMind - Geospatial AI Agent
3
3
 
4
4
  """
5
5
 
6
- __version__ = "1.0.3"
6
+ __version__ = "1.0.5"
7
7
  __author__ = "Harsh Shinde"
8
8
 
9
9
  from .agent import GeoMindAgent
@@ -12,7 +12,11 @@ from datetime import datetime
12
12
 
13
13
  from openai import OpenAI
14
14
 
15
- from .config import OPENROUTER_API_KEY, OPENROUTER_API_URL, OPENROUTER_MODEL
15
+ from .config import (
16
+ OPENROUTER_API_KEY,
17
+ OPENROUTER_API_URL,
18
+ OPENROUTER_MODEL,
19
+ )
16
20
  from .tools import (
17
21
  geocode_location,
18
22
  get_bbox_from_location,
@@ -241,8 +245,8 @@ class GeoMindAgent:
241
245
  Initialize the GeoMind agent.
242
246
 
243
247
  Args:
244
- model: Model name (default: xiaomi/mimo-v2-flash:free)
245
- api_key: OpenRouter API key. If not provided, looks for OPENROUTER_API_KEY env variable.
248
+ model: Model name (default: google/gemini-2.0-flash-exp:free)
249
+ api_key: OpenRouter API key (required).
246
250
  """
247
251
  self.provider = "openrouter"
248
252
  self.api_key = api_key or OPENROUTER_API_KEY
@@ -252,15 +256,14 @@ class GeoMindAgent:
252
256
  if not self.api_key:
253
257
  raise ValueError(
254
258
  "OpenRouter API key required.\n"
255
- "You can provide it in three ways:\n"
256
- "1. Pass it to the constructor: GeoMindAgent(api_key='your-key')\n"
257
- "2. Set OPENROUTER_API_KEY environment variable\n"
258
- "3. Create a .env file with OPENROUTER_API_KEY=your-key\n"
259
- "\nGet your API key at: https://openrouter.ai/settings/keys"
259
+ "Get your FREE API key at: https://openrouter.ai/settings/keys\n\n"
260
+ "Then provide it in one of these ways:\n"
261
+ "1. Run: geomind --api-key YOUR_KEY\n"
262
+ "2. Set environment variable: OPENROUTER_API_KEY=YOUR_KEY\n"
263
+ "3. Create .env file with: OPENROUTER_API_KEY=YOUR_KEY"
260
264
  )
261
265
 
262
- print(f"🚀 GeoMind Agent initialized with {self.model_name} (OpenRouter)")
263
- print(f" API URL: {self.base_url}")
266
+ print(f"🚀 GeoMind Agent initialized with {self.model_name}")
264
267
 
265
268
  # Create OpenAI-compatible client
266
269
  self.client = OpenAI(base_url=self.base_url, api_key=self.api_key)
@@ -296,6 +299,17 @@ When users ask for imagery:
296
299
 
297
300
  Always explain what you're doing and interpret results in a helpful way."""
298
301
 
302
+ def _call_llm(self, messages: list, tools: list) -> dict:
303
+ """Call LLM via OpenAI-compatible client."""
304
+ response = self.client.chat.completions.create(
305
+ model=self.model_name,
306
+ messages=messages,
307
+ tools=tools,
308
+ tool_choice="auto",
309
+ max_tokens=4096,
310
+ )
311
+ return response.model_dump()
312
+
299
313
  def _execute_function(self, name: str, args: dict) -> dict:
300
314
  """Execute a function call and return the result."""
301
315
  print(f" 🔧 Executing: {name}({args})")
@@ -329,42 +343,39 @@ Always explain what you're doing and interpret results in a helpful way."""
329
343
  while iteration < max_iterations:
330
344
  iteration += 1
331
345
 
332
- # Call the model
333
- response = self.client.chat.completions.create(
334
- model=self.model_name,
335
- messages=messages,
336
- tools=TOOLS,
337
- tool_choice="auto",
338
- max_tokens=4096,
339
- )
346
+ # Call the model (via proxy or direct)
347
+ response_data = self._call_llm(messages, TOOLS)
340
348
 
341
- assistant_message = response.choices[0].message
349
+ # Extract assistant message from response
350
+ choice = response_data["choices"][0]
351
+ assistant_message = choice["message"]
342
352
 
343
353
  # Check if there are tool calls
344
- if assistant_message.tool_calls:
354
+ tool_calls = assistant_message.get("tool_calls", [])
355
+ if tool_calls:
345
356
  # Add assistant message with tool calls to messages
346
357
  messages.append(
347
358
  {
348
359
  "role": "assistant",
349
- "content": assistant_message.content or "",
360
+ "content": assistant_message.get("content") or "",
350
361
  "tool_calls": [
351
362
  {
352
- "id": tc.id,
363
+ "id": tc["id"],
353
364
  "type": "function",
354
365
  "function": {
355
- "name": tc.function.name,
356
- "arguments": tc.function.arguments,
366
+ "name": tc["function"]["name"],
367
+ "arguments": tc["function"]["arguments"],
357
368
  },
358
369
  }
359
- for tc in assistant_message.tool_calls
370
+ for tc in tool_calls
360
371
  ],
361
372
  }
362
373
  )
363
374
 
364
375
  # Execute each tool call
365
- for tool_call in assistant_message.tool_calls:
366
- func_name = tool_call.function.name
367
- func_args = json.loads(tool_call.function.arguments)
376
+ for tool_call in tool_calls:
377
+ func_name = tool_call["function"]["name"]
378
+ func_args = json.loads(tool_call["function"]["arguments"])
368
379
 
369
380
  result = self._execute_function(func_name, func_args)
370
381
 
@@ -372,13 +383,13 @@ Always explain what you're doing and interpret results in a helpful way."""
372
383
  messages.append(
373
384
  {
374
385
  "role": "tool",
375
- "tool_call_id": tool_call.id,
386
+ "tool_call_id": tool_call["id"],
376
387
  "content": json.dumps(result, default=str),
377
388
  }
378
389
  )
379
390
  else:
380
391
  # No tool calls, we have a final response
381
- final_text = assistant_message.content or ""
392
+ final_text = assistant_message.get("content") or ""
382
393
 
383
394
  # Add to history
384
395
  self.history.append({"role": "assistant", "content": final_text})
@@ -3,12 +3,39 @@ Command-line interface for GeoMind.
3
3
  """
4
4
 
5
5
  import sys
6
+ import os
6
7
  import argparse
8
+ from pathlib import Path
7
9
  from typing import Optional
8
10
 
9
11
  from .agent import GeoMindAgent
10
12
 
11
13
 
14
+ # Config file path for storing API key
15
+ CONFIG_DIR = Path.home() / ".geomind"
16
+ CONFIG_FILE = CONFIG_DIR / "config"
17
+
18
+
19
+ def get_saved_api_key() -> Optional[str]:
20
+ """Get API key saved on user's PC."""
21
+ if CONFIG_FILE.exists():
22
+ try:
23
+ return CONFIG_FILE.read_text().strip()
24
+ except Exception:
25
+ return None
26
+ return None
27
+
28
+
29
+ def save_api_key(api_key: str) -> bool:
30
+ """Save API key to user's PC."""
31
+ try:
32
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
33
+ CONFIG_FILE.write_text(api_key)
34
+ return True
35
+ except Exception:
36
+ return False
37
+
38
+
12
39
  def main():
13
40
  """Main CLI entry point for the geomind package."""
14
41
  parser = argparse.ArgumentParser(
@@ -56,9 +83,20 @@ Environment Variables:
56
83
  parser.add_argument(
57
84
  "--version", "-v", action="store_true", help="Show version and exit"
58
85
  )
86
+ parser.add_argument(
87
+ "--clear-key", action="store_true", help="Clear saved API key"
88
+ )
59
89
 
60
90
  args = parser.parse_args()
61
91
 
92
+ if args.clear_key:
93
+ if CONFIG_FILE.exists():
94
+ CONFIG_FILE.unlink()
95
+ print("✅ Saved API key cleared.")
96
+ else:
97
+ print("ℹ️ No saved API key found.")
98
+ sys.exit(0)
99
+
62
100
  if args.version:
63
101
  from . import __version__
64
102
 
@@ -68,8 +106,12 @@ Environment Variables:
68
106
  # Start interactive or single-query mode
69
107
  try:
70
108
  if args.query:
71
- # Single query mode
72
- agent = GeoMindAgent(model=args.model, api_key=args.api_key)
109
+ # Single query mode - also use saved key
110
+ api_key = args.api_key or get_saved_api_key()
111
+ if not api_key:
112
+ print("❌ No API key found. Run 'geomind' first to set up.")
113
+ sys.exit(1)
114
+ agent = GeoMindAgent(model=args.model, api_key=api_key)
73
115
  agent.chat(args.query)
74
116
  else:
75
117
  # Interactive mode
@@ -95,6 +137,30 @@ def run_interactive(model: Optional[str] = None, api_key: Optional[str] = None):
95
137
  print("Type 'reset' to start a new conversation")
96
138
  print("=" * 60)
97
139
 
140
+ # Check for API key in order: argument > env > saved file
141
+ from .config import OPENROUTER_API_KEY
142
+
143
+ if not api_key:
144
+ api_key = OPENROUTER_API_KEY
145
+
146
+ if not api_key:
147
+ api_key = get_saved_api_key()
148
+
149
+ if not api_key:
150
+ print("\n🔑 OpenRouter API key required (FREE)")
151
+ print(" Get yours at: https://openrouter.ai/settings/keys\n")
152
+ api_key = input(" Enter your API key: ").strip()
153
+
154
+ if not api_key:
155
+ print("\n❌ No API key provided. Exiting.")
156
+ return
157
+
158
+ # Save the key for future use
159
+ if save_api_key(api_key):
160
+ print(" ✅ API key saved! You won't need to enter it again.\n")
161
+ else:
162
+ print(" ⚠️ Could not save API key. You'll need to enter it next time.\n")
163
+
98
164
  agent = GeoMindAgent(model=model, api_key=api_key)
99
165
 
100
166
  while True:
@@ -49,7 +49,7 @@ OUTPUT_DIR.mkdir(exist_ok=True)
49
49
  GEOCODER_USER_AGENT = "geomind_agent_v0.1"
50
50
 
51
51
  # OpenRouter API Configuration
52
- # OpenRouter provides access to multiple AI models via API
53
- OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
52
+ # Get your free API key at: https://openrouter.ai/settings/keys
53
+ OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "")
54
54
  OPENROUTER_API_URL = os.getenv("OPENROUTER_API_URL", "https://openrouter.ai/api/v1")
55
- OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL", "xiaomi/mimo-v2-flash:free")
55
+ OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL", "google/gemini-2.0-flash-exp:free")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geomind-ai
3
- Version: 1.0.3
3
+ Version: 1.0.5
4
4
  Summary: AI agent for geospatial analysis with Sentinel-2 satellite imagery
5
5
  Author: Harsh Shinde
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "geomind-ai"
7
- version = "1.0.3"
7
+ version = "1.0.5"
8
8
  description = "AI agent for geospatial analysis with Sentinel-2 satellite imagery"
9
9
  readme = "README.md"
10
10
  authors = [
File without changes
File without changes
File without changes
File without changes
File without changes