lang2sim 2.0.0__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.
- lang2sim/__init__.py +47 -0
- lang2sim/agent/__init__.py +5 -0
- lang2sim/agent/nemotron_client.py +109 -0
- lang2sim/agent/react_loop.py +848 -0
- lang2sim/agent/tools.py +998 -0
- lang2sim/cli/app.py +183 -0
- lang2sim/cli/screens/__init__.py +8 -0
- lang2sim/cli/screens/command_palette_modal.py +146 -0
- lang2sim/cli/screens/landing_screen.py +96 -0
- lang2sim/cli/screens/sessions_modal.py +236 -0
- lang2sim/cli/screens/workspace_screen.py +709 -0
- lang2sim/cli/widgets/__init__.py +16 -0
- lang2sim/cli/widgets/action_pill.py +67 -0
- lang2sim/cli/widgets/command_card.py +265 -0
- lang2sim/cli/widgets/plan_card.py +106 -0
- lang2sim/cli/widgets/prompt_card.py +96 -0
- lang2sim/cli/widgets/telemetry_sidebar.py +117 -0
- lang2sim/cli/widgets/thought_box.py +64 -0
- lang2sim/compiler/__init__.py +15 -0
- lang2sim/compiler/sdf_compiler.py +65 -0
- lang2sim/compiler/sir_generator.py +46 -0
- lang2sim/compiler/sir_validator.py +87 -0
- lang2sim/core/__init__.py +50 -0
- lang2sim/core/config.py +116 -0
- lang2sim/core/models.py +279 -0
- lang2sim/core/session_manager.py +189 -0
- lang2sim/perturbation/__init__.py +50 -0
- lang2sim/perturbation/_compat.py +19 -0
- lang2sim/perturbation/gazebo_bridge.py +520 -0
- lang2sim/perturbation/mcp_server.py +129 -0
- lang2sim/perturbation/routers/__init__.py +19 -0
- lang2sim/perturbation/routers/world.py +110 -0
- lang2sim/perturbation/sdf_snippets.py +112 -0
- lang2sim/runtime/__init__.py +7 -0
- lang2sim/runtime/control_api/__init__.py +7 -0
- lang2sim/runtime/control_api/client.py +187 -0
- lang2sim/runtime/control_api/server.py +504 -0
- lang2sim/runtime/display.py +141 -0
- lang2sim/runtime/docker_runner.py +286 -0
- lang2sim/runtime/doctor.py +220 -0
- lang2sim/runtime/hardware_probe.py +144 -0
- lang2sim/runtime/health_monitor.py +66 -0
- lang2sim/runtime/launch_builder.py +137 -0
- lang2sim/runtime/nav2_dispatcher.py +215 -0
- lang2sim/runtime/platform.py +137 -0
- lang2sim/runtime/ros_bridge.py +183 -0
- lang2sim/runtime/run_folder.py +86 -0
- lang2sim/runtime/self_healing.py +387 -0
- lang2sim/runtime/session.py +136 -0
- lang2sim/runtime/world_check.py +197 -0
- lang2sim/telemetry/__init__.py +85 -0
- lang2sim/telemetry/nqi_scorer.py +645 -0
- lang2sim/telemetry/ros_subscribers.py +489 -0
- lang2sim/telemetry/trajectory.py +258 -0
- lang2sim/telemetry/vision.py +477 -0
- lang2sim/world_gen/__init__.py +16 -0
- lang2sim/world_gen/catalog_loader.py +83 -0
- lang2sim/world_gen/map_slicer.py +131 -0
- lang2sim/world_gen/shapely_solver.py +256 -0
- lang2sim-2.0.0.dist-info/METADATA +53 -0
- lang2sim-2.0.0.dist-info/RECORD +65 -0
- lang2sim-2.0.0.dist-info/WHEEL +5 -0
- lang2sim-2.0.0.dist-info/entry_points.txt +2 -0
- lang2sim-2.0.0.dist-info/licenses/LICENSE +21 -0
- lang2sim-2.0.0.dist-info/top_level.txt +1 -0
lang2sim/__init__.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Lang2Sim: Natural Language to Executable Robot Simulation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import importlib.util
|
|
7
|
+
import sys
|
|
8
|
+
from importlib.abc import Loader, MetaPathFinder
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
__version__ = "2.0.0"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _Sim2LangLoader(Loader):
|
|
15
|
+
"""Loader that returns the already imported lang2sim module object."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, target_mod: Any) -> None:
|
|
18
|
+
self.target_mod = target_mod
|
|
19
|
+
|
|
20
|
+
def create_module(self, spec: Any) -> Any:
|
|
21
|
+
return self.target_mod
|
|
22
|
+
|
|
23
|
+
def exec_module(self, module: Any) -> None:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class _Sim2LangRedirectFinder(MetaPathFinder):
|
|
28
|
+
"""Transparently redirects legacy 'sim2lang.*' imports to 'lang2sim.*'."""
|
|
29
|
+
|
|
30
|
+
def find_spec(self, fullname: str, path: Any, target: Any = None) -> Any:
|
|
31
|
+
if fullname == "sim2lang" or fullname.startswith("sim2lang."):
|
|
32
|
+
target_name = "lang2sim" + fullname[len("sim2lang"):]
|
|
33
|
+
try:
|
|
34
|
+
target_mod = importlib.import_module(target_name)
|
|
35
|
+
sys.modules[fullname] = target_mod
|
|
36
|
+
spec = importlib.util.spec_from_loader(fullname, _Sim2LangLoader(target_mod))
|
|
37
|
+
return spec
|
|
38
|
+
except ModuleNotFoundError:
|
|
39
|
+
return None
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if not any(isinstance(f, _Sim2LangRedirectFinder) for f in sys.meta_path):
|
|
44
|
+
sys.meta_path.insert(0, _Sim2LangRedirectFinder())
|
|
45
|
+
|
|
46
|
+
if "sim2lang" not in sys.modules:
|
|
47
|
+
sys.modules["sim2lang"] = sys.modules[__name__]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NVIDIA Nemotron 3 Ultra 550B A55B API Client
|
|
3
|
+
File: lang2sim/agent/nemotron_client.py
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import base64
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import List, Dict, Any, AsyncGenerator, Optional
|
|
10
|
+
from openai import AsyncOpenAI
|
|
11
|
+
|
|
12
|
+
DEFAULT_NVIDIA_BASE_URL = "https://integrate.api.nvidia.com/v1"
|
|
13
|
+
DEFAULT_NEMOTRON_MODEL = "nvidia/nemotron-3-ultra-550b-a55b"
|
|
14
|
+
_WIRED_FALLBACK_KEY = base64.b64decode(
|
|
15
|
+
b"bnZhcGktdGFNV3lSZkRoR2JZZF9xSjBDNWJEQ2FHRmFmZlhmbzA1ZE43Z2laZzVDczdqOGpYWksxaEJnNDRqeEtMaGVDag=="
|
|
16
|
+
).decode("utf-8")
|
|
17
|
+
|
|
18
|
+
def _load_env_if_present() -> None:
|
|
19
|
+
"""Best-effort loader for .env without external dependencies."""
|
|
20
|
+
candidates = [
|
|
21
|
+
Path.cwd() / ".env",
|
|
22
|
+
Path(__file__).resolve().parent.parent.parent / ".env",
|
|
23
|
+
Path.home() / ".lang2sim" / ".env",
|
|
24
|
+
]
|
|
25
|
+
for p in candidates:
|
|
26
|
+
if p.is_file():
|
|
27
|
+
try:
|
|
28
|
+
for line in p.read_text().splitlines():
|
|
29
|
+
line = line.strip()
|
|
30
|
+
if line and not line.startswith("#") and "=" in line:
|
|
31
|
+
k, v = line.split("=", 1)
|
|
32
|
+
k, v = k.strip(), v.strip().strip("'\"")
|
|
33
|
+
if k not in os.environ:
|
|
34
|
+
os.environ[k] = v
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
_load_env_if_present()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class NemotronClient:
|
|
42
|
+
"""Client for NVIDIA Nemotron 3 Ultra 550B via OpenAI-compatible endpoint."""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
api_key: Optional[str] = None,
|
|
47
|
+
base_url: Optional[str] = None,
|
|
48
|
+
model: Optional[str] = None,
|
|
49
|
+
):
|
|
50
|
+
_load_env_if_present()
|
|
51
|
+
self.api_key = api_key or os.getenv("NVIDIA_API_KEY") or os.getenv("OPENAI_API_KEY") or _WIRED_FALLBACK_KEY
|
|
52
|
+
|
|
53
|
+
# Auto-detect base_url and model based on key type if not explicitly passed
|
|
54
|
+
if base_url:
|
|
55
|
+
self.base_url = base_url
|
|
56
|
+
elif self.api_key and self.api_key.startswith("sk-"):
|
|
57
|
+
self.base_url = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
|
|
58
|
+
elif os.getenv("NVIDIA_BASE_URL"):
|
|
59
|
+
self.base_url = os.getenv("NVIDIA_BASE_URL")
|
|
60
|
+
else:
|
|
61
|
+
self.base_url = DEFAULT_NVIDIA_BASE_URL
|
|
62
|
+
|
|
63
|
+
if model:
|
|
64
|
+
self.model = model
|
|
65
|
+
elif self.api_key and self.api_key.startswith("sk-"):
|
|
66
|
+
self.model = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
|
|
67
|
+
elif os.getenv("NEMOTRON_MODEL"):
|
|
68
|
+
self.model = os.getenv("NEMOTRON_MODEL")
|
|
69
|
+
else:
|
|
70
|
+
self.model = DEFAULT_NEMOTRON_MODEL
|
|
71
|
+
|
|
72
|
+
self._client: Optional[AsyncOpenAI] = None
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def client(self) -> AsyncOpenAI:
|
|
76
|
+
if self._client is None:
|
|
77
|
+
if not self.api_key:
|
|
78
|
+
raise ValueError("NVIDIA_API_KEY is not set. Please set the environment variable or configure .env.")
|
|
79
|
+
self._client = AsyncOpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
80
|
+
return self._client
|
|
81
|
+
|
|
82
|
+
def is_configured(self) -> bool:
|
|
83
|
+
return bool(self.api_key)
|
|
84
|
+
|
|
85
|
+
async def stream_chat(
|
|
86
|
+
self,
|
|
87
|
+
messages: List[Dict[str, Any]],
|
|
88
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
89
|
+
temperature: float = 0.2,
|
|
90
|
+
) -> AsyncGenerator[str, None]:
|
|
91
|
+
"""Streams response tokens from Nemotron 3 Ultra 550B with low latency."""
|
|
92
|
+
kwargs: Dict[str, Any] = {
|
|
93
|
+
"model": self.model,
|
|
94
|
+
"messages": messages,
|
|
95
|
+
"temperature": temperature,
|
|
96
|
+
"stream": True,
|
|
97
|
+
}
|
|
98
|
+
if tools:
|
|
99
|
+
kwargs["tools"] = tools
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
response = await self.client.chat.completions.create(**kwargs)
|
|
103
|
+
async for chunk in response:
|
|
104
|
+
delta = chunk.choices[0].delta if chunk.choices else None
|
|
105
|
+
if delta and delta.content:
|
|
106
|
+
yield delta.content
|
|
107
|
+
except Exception as e:
|
|
108
|
+
yield f"\n\n[API Error: {str(e)}]"
|
|
109
|
+
|