Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,21 @@ Then you need to set the weights path in the file `jarvis/steveI/path.py`.

## Usage

You need to set the environment variable `TMPDIR` and `OPENAI_API_KEY` first.
You need to set the environment variable `TMPDIR` and your MiniMax API key first.
```bash
export TMPDIR=/tmp
export OPENAI_API_KEY="sk-******"
export MINIMAX_API_KEY="******"
```

The planner talks to the MiniMax OpenAI-compatible API. You can optionally
select the region, model, or an explicit base URL:
```bash
# Region selects the endpoint: "global_en" (default) or "cn_zh".
export MINIMAX_REGION="global_en"
# Model id: "MiniMax-M3" (default) or "MiniMax-M2.7".
export MINIMAX_MODEL="MiniMax-M3"
# Optional: override the base URL directly (takes precedence over MINIMAX_REGION).
export MINIMAX_BASE_URL="https://api.minimax.io/v1"
```
### Learning with dynamic memory (Coming Soon)

Expand Down
52 changes: 51 additions & 1 deletion jarvis/assembly/base.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,57 @@
from jarvis.utils import write_video

import os
from openai import OpenAI
client = OpenAI()

# MiniMax exposes an OpenAI-compatible API. The global and mainland-China
# endpoints share the same request/response schema but live on different hosts,
# so the client is configured by region instead of being hardcoded.
MINIMAX_BASE_URLS = {
"global_en": "https://api.minimax.io/v1",
"cn_zh": "https://api.minimaxi.com/v1",
}
DEFAULT_REGION = "global_en"

# Supported MiniMax text models; the first entry is used as the default.
MINIMAX_MODELS = ["MiniMax-M3", "MiniMax-M2.7"]
DEFAULT_MODEL = MINIMAX_MODELS[0]


def get_base_url():
"""Resolve the MiniMax OpenAI-compatible base URL.

``MINIMAX_BASE_URL`` takes precedence when set; otherwise ``MINIMAX_REGION``
selects between the global (``global_en``) and mainland-China (``cn_zh``)
endpoints, defaulting to the global endpoint.
"""
base_url = os.environ.get("MINIMAX_BASE_URL")
if base_url:
return base_url
region = os.environ.get("MINIMAX_REGION", DEFAULT_REGION)
return MINIMAX_BASE_URLS.get(region, MINIMAX_BASE_URLS[DEFAULT_REGION])


def get_model():
"""Resolve the MiniMax text model id from ``MINIMAX_MODEL``.

Defaults to :data:`DEFAULT_MODEL` when the variable is unset.
"""
return os.environ.get("MINIMAX_MODEL", DEFAULT_MODEL)


def build_client():
"""Create an OpenAI-compatible client pointed at the MiniMax API.

The API key is read from ``MINIMAX_API_KEY`` and falls back to
``OPENAI_API_KEY`` for backward compatibility.
"""
return OpenAI(
api_key=os.environ.get("MINIMAX_API_KEY") or os.environ.get("OPENAI_API_KEY"),
base_url=get_base_url(),
)


client = build_client()
Comment on lines +42 to +54

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)base\.py$|jarvis/assembly|requirements|pyproject|poetry|setup|environment|env' || true

echo "== target file =="
if [ -f jarvis/assembly/base.py ]; then
  nl -ba jarvis/assembly/base.py | sed -n '1,120p'
fi

echo "== search env/client usage =="
rg -n 'MINIMAX_API_KEY|OPENAI_API_KEY|build_client|base_url\s*=|OpenAI|openai' -S . --glob '!__pycache__/**' --glob '!*.pyc' || true

Repository: CraftJarvis/JARVIS-1

Length of output: 3358


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== target file =="
cat -n jarvis/assembly/base.py | sed -n '1,140p'

echo "== relevant usage =="
rg -n 'MINIMAX_API_KEY|OPENAI_API_KEY|build_client\(|from jarvis\.assembly\.base import|import jarvis\.assembly\.base|OpenAI\(' -S . --glob '!__pycache__/**' --glob '!*.pyc' || true

echo "== pyproject deps =="
if [ -f pyproject.toml ]; then
  rg -n 'openai|minimax|python' pyproject.toml -C 3 || true
fi

Repository: CraftJarvis/JARVIS-1

Length of output: 5332


Require MINIMAX_API_KEY for MiniMax requests.

get_base_url() always resolves a MiniMax base_url, but build_client() falls back to OPENAI_API_KEY when MINIMAX_API_KEY is missing. If OPENAI_API_KEY is set, the client can send that OpenAI credential to the MiniMax endpoint; require MINIMAX_API_KEY or gate the fallback behind an explicit provider selection.

Proposed fix
 def build_client():
+    api_key = os.environ.get("MINIMAX_API_KEY")
+    if not api_key:
+        raise RuntimeError("MINIMAX_API_KEY is required")
     return OpenAI(
-        api_key=os.environ.get("MINIMAX_API_KEY") or os.environ.get("OPENAI_API_KEY"),
+        api_key=api_key,
         base_url=get_base_url(),
     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def build_client():
"""Create an OpenAI-compatible client pointed at the MiniMax API.
The API key is read from ``MINIMAX_API_KEY`` and falls back to
``OPENAI_API_KEY`` for backward compatibility.
"""
return OpenAI(
api_key=os.environ.get("MINIMAX_API_KEY") or os.environ.get("OPENAI_API_KEY"),
base_url=get_base_url(),
)
client = build_client()
def build_client():
"""Create an OpenAI-compatible client pointed at the MiniMax API.
The API key is read from ``MINIMAX_API_KEY`` and falls back to
``OPENAI_API_KEY`` for backward compatibility.
"""
api_key = os.environ.get("MINIMAX_API_KEY")
if not api_key:
raise RuntimeError("MINIMAX_API_KEY is required")
return OpenAI(
api_key=api_key,
base_url=get_base_url(),
)
client = build_client()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jarvis/assembly/base.py` around lines 42 - 54, Update build_client to require
MINIMAX_API_KEY whenever get_base_url() targets the MiniMax endpoint; remove the
unconditional OPENAI_API_KEY fallback or gate it behind an explicit provider
selection, ensuring MiniMax requests cannot use an OpenAI credential.


import json
from jarvis.assets import TASKS_FILE, TAG_ITEMS_FILE, SPAWN_FILE, SKILL_FILE
Expand Down
8 changes: 4 additions & 4 deletions jarvis/assembly/core.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from jarvis.assembly.base import client
from jarvis.assembly.base import client, get_model
from jarvis.assembly.base import skills
import random

Expand Down Expand Up @@ -59,7 +59,7 @@ def get_skill(task, info):
print("query: ", query)

response = client.chat.completions.create(
model="gpt-3.5-turbo",
model=get_model(),
messages=[
{
"role": "system",
Expand Down Expand Up @@ -107,8 +107,8 @@ def get_skill(task, info):
return skills[task][action_index-1]

class JARVIS:
def __init__(self, model = 'gpt-3.5-turbo'):
self.model = model
def __init__(self, model=None):
self.model = model if model is not None else get_model()

# TODO: add online generating plans function
# zhwang4ai: release online planning agent in the next version
57 changes: 57 additions & 0 deletions tests/test_minimax_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Tests for the configurable MiniMax provider setup in ``jarvis.assembly.base``."""

import importlib

import pytest


@pytest.fixture(autouse=True)
def _clean_env(monkeypatch):
# A placeholder key lets the OpenAI-compatible client be constructed at
# import time without contacting the network.
monkeypatch.setenv("MINIMAX_API_KEY", "test-key")
for var in ("MINIMAX_BASE_URL", "MINIMAX_REGION", "MINIMAX_MODEL", "OPENAI_API_KEY"):
monkeypatch.delenv(var, raising=False)


def _load_base():
import jarvis.assembly.base as base

return importlib.reload(base)


def test_default_region_is_global():
base = _load_base()
assert base.DEFAULT_REGION == "global_en"
assert base.get_base_url() == "https://api.minimax.io/v1"


def test_cn_region_uses_mainland_endpoint(monkeypatch):
monkeypatch.setenv("MINIMAX_REGION", "cn_zh")
base = _load_base()
assert base.get_base_url() == "https://api.minimaxi.com/v1"


def test_explicit_base_url_overrides_region(monkeypatch):
monkeypatch.setenv("MINIMAX_REGION", "cn_zh")
monkeypatch.setenv("MINIMAX_BASE_URL", "https://example.test/v1")
base = _load_base()
assert base.get_base_url() == "https://example.test/v1"


def test_default_model():
base = _load_base()
assert base.MINIMAX_MODELS[:2] == ["MiniMax-M3", "MiniMax-M2.7"]
assert base.DEFAULT_MODEL == "MiniMax-M3"
assert base.get_model() == "MiniMax-M3"


def test_model_selection(monkeypatch):
monkeypatch.setenv("MINIMAX_MODEL", "MiniMax-M2.7")
base = _load_base()
assert base.get_model() == "MiniMax-M2.7"


def test_client_points_at_resolved_base_url():
base = _load_base()
assert str(base.client.base_url).rstrip("/") == "https://api.minimax.io/v1"