openbase-coder 0.1.3__tar.gz → 0.1.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.
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/PKG-INFO +1 -1
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/_version.py +2 -2
- openbase_coder-0.1.5/openbase_coder_cli/brain_score.py +27 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cartesia_voice_catalog.py +6 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/node.py +13 -2
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/setup.py +227 -2
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/livekit.py +29 -77
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_voice_route.py +7 -55
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/brain_readiness.py +21 -10
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/.gitignore +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/LICENSE +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/README.md +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/__init__.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/__main__.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/__init__.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/auth.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/boilersync.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/bootstrap.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/claude_chrome.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/codex_sync.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/computer_use.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/doctor.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/local_server.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/plugins.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/restart.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/routines.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/server.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/services.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/super_agent_name.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/user.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli/utils.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/cli_helpers.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/config/__init__.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/config/asgi.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/config/authentication.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/config/jwt_validation.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/config/proxy.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/config/serializers.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/config/settings.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/config/token_manager.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/config/urls.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/config/viewsets.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/config/wsgi.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/dispatcher_config.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/entrypoint/manage.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/errors.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/__init__.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/codex_app_client.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/codex_thread_state.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/codex_transport.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/codex_turns.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/speech_formatter.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/speech_replacements.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_announcer.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_start_announcer.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_voice_history.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/logging.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/mcp/__init__.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/mcp/apps.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/mcp/mcp.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/mcp/models.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/mcp/projects.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/mcp/session_manager.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/mcp/thread_import.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/multi_config.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/__init__.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/agents_md.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/approvals.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/apps.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/auth.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/common.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/consumers.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/diagnostics.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/livekit.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/middleware.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/migrations/0001_initial.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/migrations/0002_remove_cron_models.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/migrations/__init__.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/models.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/plugins_tools.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/projects.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/reports.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/routines.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/routing.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/serializers.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/services_views.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/skills.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/thread_cache.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/thread_metadata.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/threads.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/urls.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/views.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/paths.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/plugins/__init__.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/plugins/api.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/plugins/console.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/plugins/install.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/plugins/manager.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/plugins/models.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/plugins/skills.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/plugins/sources.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/plugins/spec.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/plugins/store.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/__init__.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/boilersync.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/console_settings.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/definitions.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/installation.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/launchctl_tools.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/launchd.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/openbase_services.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/registry.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/restart.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/tailnet_devices.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/uv_tools.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/voice_warning.py +0 -0
- {openbase_coder-0.1.3 → openbase_coder-0.1.5}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openbase-coder
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: OpenBase Coder CLI with embedded server
|
|
5
5
|
Project-URL: Repository, https://github.com/openbase-community/openbase-coder
|
|
6
6
|
Project-URL: Issues, https://github.com/openbase-community/openbase-coder/issues
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.1.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
21
|
+
__version__ = version = '0.1.5'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 5)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def brain_score_token_file() -> Path:
|
|
8
|
+
return Path(
|
|
9
|
+
os.getenv(
|
|
10
|
+
"OPENBASE_BRAIN_SCORE_TOKEN_FILE",
|
|
11
|
+
str(Path.home() / ".openbase" / "brain_score_token"),
|
|
12
|
+
)
|
|
13
|
+
).expanduser()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def load_brain_score_token() -> str:
|
|
17
|
+
configured = os.getenv("OPENBASE_BRAIN_SCORE_TOKEN", "").strip()
|
|
18
|
+
if configured:
|
|
19
|
+
return configured
|
|
20
|
+
try:
|
|
21
|
+
return brain_score_token_file().read_text(encoding="utf-8").strip()
|
|
22
|
+
except (FileNotFoundError, OSError):
|
|
23
|
+
return ""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def brain_score_token_configured() -> bool:
|
|
27
|
+
return bool(load_brain_score_token())
|
|
@@ -96,6 +96,12 @@ CARTESIA_VOICE_CATALOG: tuple[CartesiaVoiceCatalogEntry, ...] = (
|
|
|
96
96
|
CartesiaVoiceCatalogEntry("91b4cf29-5166-44eb-8054-30d40ecc8081", "Tina", "en", "US", "feminine"),
|
|
97
97
|
)
|
|
98
98
|
|
|
99
|
+
DEFAULT_SUPER_AGENT_VOICE_IDS = tuple(
|
|
100
|
+
voice.id
|
|
101
|
+
for voice in CARTESIA_VOICE_CATALOG
|
|
102
|
+
if voice.id != "9626c31c-bec5-4cca-baa8-f8ba9e84c8bc"
|
|
103
|
+
)
|
|
104
|
+
|
|
99
105
|
_CATALOG_BY_ID = {voice.id: voice for voice in CARTESIA_VOICE_CATALOG}
|
|
100
106
|
_CATALOG_BY_NORMALIZED_NAME = {
|
|
101
107
|
" ".join(voice.name.casefold().split()): voice for voice in CARTESIA_VOICE_CATALOG
|
|
@@ -24,6 +24,7 @@ def run_workspace_package_command(workspace_dir: Path, package_dir: Path, *args:
|
|
|
24
24
|
subprocess.run(
|
|
25
25
|
[executable, *command_prefix, *command_args],
|
|
26
26
|
cwd=str(package_dir),
|
|
27
|
+
env=_package_manager_env(executable, command_args),
|
|
27
28
|
check=True,
|
|
28
29
|
)
|
|
29
30
|
return True
|
|
@@ -93,12 +94,22 @@ def _normalize_package_manager_args(
|
|
|
93
94
|
workspace_dir: Path,
|
|
94
95
|
args: tuple[str, ...],
|
|
95
96
|
) -> tuple[str, ...]:
|
|
96
|
-
"""
|
|
97
|
+
"""Avoid mutating or validating partial install-set lockfiles with pnpm."""
|
|
97
98
|
if (
|
|
98
99
|
Path(executable).name == "pnpm"
|
|
99
100
|
and args == ("install",)
|
|
100
101
|
and (workspace_dir / "pnpm-lock.yaml").is_file()
|
|
101
102
|
):
|
|
102
|
-
return ("install", "--
|
|
103
|
+
return ("install", "--no-lockfile", "--shamefully-hoist")
|
|
103
104
|
|
|
104
105
|
return args
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _package_manager_env(
|
|
109
|
+
executable: str,
|
|
110
|
+
args: tuple[str, ...],
|
|
111
|
+
) -> dict[str, str] | None:
|
|
112
|
+
if Path(executable).name == "pnpm" and args and args[0] == "install":
|
|
113
|
+
return {**os.environ, "CI": "true"}
|
|
114
|
+
|
|
115
|
+
return None
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import platform
|
|
4
5
|
import secrets
|
|
5
6
|
import shlex
|
|
@@ -14,6 +15,7 @@ from openbase_coder_cli.cli.node import run_workspace_package_command
|
|
|
14
15
|
from openbase_coder_cli.paths import (
|
|
15
16
|
CODEX_AGENTS_MD_PATH,
|
|
16
17
|
CODEX_DIRECT_LIVEKIT_INSTRUCTIONS_PATH,
|
|
18
|
+
CODEX_DISPATCHER_CONFIG_PATH,
|
|
17
19
|
CODEX_DISPATCHER_INSTRUCTIONS_PATH,
|
|
18
20
|
CODEX_HOME_DIR,
|
|
19
21
|
CODEX_SUPER_AGENT_INSTRUCTIONS_PATH,
|
|
@@ -34,6 +36,21 @@ CODEX_HOME_DEFAULT_FILES = (
|
|
|
34
36
|
("DISPATCHER_INSTRUCTIONS.md", CODEX_DISPATCHER_INSTRUCTIONS_PATH),
|
|
35
37
|
("SUPER_AGENT_INSTRUCTIONS.md", CODEX_SUPER_AGENT_INSTRUCTIONS_PATH),
|
|
36
38
|
)
|
|
39
|
+
SUPER_AGENTS_MCP_TABLE = "mcp_servers.super-agents"
|
|
40
|
+
SUPER_AGENTS_MCP_COMMAND = "super-agents-mcp"
|
|
41
|
+
CODEX_HOME_PERMISSION_VALUES = (
|
|
42
|
+
("sandbox_mode", json.dumps("danger-full-access")),
|
|
43
|
+
(
|
|
44
|
+
"approval_policy",
|
|
45
|
+
"{ granular = { sandbox_approval = false, rules = false, "
|
|
46
|
+
"mcp_elicitations = false, request_permissions = false, "
|
|
47
|
+
"skill_approval = false } }",
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
CODEX_HOME_DEFAULT_DISPATCHER_CONFIG = {
|
|
51
|
+
"dispatcher_reasoning_effort": "low",
|
|
52
|
+
"super_agents_reasoning_effort": "high",
|
|
53
|
+
}
|
|
37
54
|
|
|
38
55
|
|
|
39
56
|
@click.command()
|
|
@@ -109,11 +126,15 @@ def setup(
|
|
|
109
126
|
# --- Symlink Codex auth into the service CODEX_HOME ---
|
|
110
127
|
_symlink_codex_auth()
|
|
111
128
|
_ensure_codex_home_default_files(workspace_dir)
|
|
129
|
+
_ensure_codex_home_dispatcher_config()
|
|
112
130
|
_symlink_codex_home_skills(workspace_dir)
|
|
113
131
|
|
|
114
132
|
# --- Initialize CLI workspace ---
|
|
115
133
|
_init_cli_workspace(workspace_dir)
|
|
116
134
|
|
|
135
|
+
# --- Configure the service CODEX_HOME ---
|
|
136
|
+
_ensure_codex_home_config(workspace_dir)
|
|
137
|
+
|
|
117
138
|
# --- Install/update user-facing CLI shim ---
|
|
118
139
|
_install_cli_shim(workspace_dir)
|
|
119
140
|
|
|
@@ -142,7 +163,9 @@ def _clone_workspace(workspace_dir: str) -> None:
|
|
|
142
163
|
if ws.exists() and (ws / ".git").is_dir():
|
|
143
164
|
click.echo(f"Workspace already exists at {ws}, pulling latest...")
|
|
144
165
|
_update_existing_workspace(ws)
|
|
166
|
+
_remove_managed_repo_symlinks(ws)
|
|
145
167
|
_multi_sync(ws)
|
|
168
|
+
_update_install_set_repos(ws)
|
|
146
169
|
return
|
|
147
170
|
|
|
148
171
|
click.echo(f"Cloning workspace to {ws}...")
|
|
@@ -151,6 +174,7 @@ def _clone_workspace(workspace_dir: str) -> None:
|
|
|
151
174
|
check=True,
|
|
152
175
|
)
|
|
153
176
|
_multi_sync(ws)
|
|
177
|
+
_update_install_set_repos(ws)
|
|
154
178
|
|
|
155
179
|
|
|
156
180
|
def _update_existing_workspace(ws: Path) -> None:
|
|
@@ -176,6 +200,57 @@ def _update_existing_workspace(ws: Path) -> None:
|
|
|
176
200
|
subprocess.run(["git", "-C", str(ws), "pull", "--ff-only"], check=True)
|
|
177
201
|
|
|
178
202
|
|
|
203
|
+
def _remove_managed_repo_symlinks(ws: Path) -> None:
|
|
204
|
+
if ws.resolve() != DEFAULT_WORKSPACE_DIR.resolve():
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
for repo_name in _install_set_repo_names(ws):
|
|
208
|
+
repo_path = ws / repo_name
|
|
209
|
+
if repo_path.is_symlink():
|
|
210
|
+
click.echo(f"Removing symlinked install repo at {repo_path}")
|
|
211
|
+
repo_path.unlink()
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _update_install_set_repos(ws: Path) -> None:
|
|
215
|
+
for repo_name in _install_set_repo_names(ws):
|
|
216
|
+
repo_path = ws / repo_name
|
|
217
|
+
if not (repo_path / ".git").exists():
|
|
218
|
+
continue
|
|
219
|
+
|
|
220
|
+
if ws.resolve() == DEFAULT_WORKSPACE_DIR.resolve():
|
|
221
|
+
click.echo(f"Updating managed install repo {repo_name}...")
|
|
222
|
+
subprocess.run(
|
|
223
|
+
["git", "-C", str(repo_path), "fetch", "origin", "main"],
|
|
224
|
+
check=True,
|
|
225
|
+
)
|
|
226
|
+
subprocess.run(
|
|
227
|
+
["git", "-C", str(repo_path), "reset", "--hard", "origin/main"],
|
|
228
|
+
check=True,
|
|
229
|
+
)
|
|
230
|
+
else:
|
|
231
|
+
subprocess.run(["git", "-C", str(repo_path), "pull", "--ff-only"], check=True)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _install_set_repo_names(ws: Path) -> list[str]:
|
|
235
|
+
multi_json_path = ws / "multi.json"
|
|
236
|
+
if not multi_json_path.is_file():
|
|
237
|
+
return []
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
multi_json = json.loads(multi_json_path.read_text(encoding="utf-8"))
|
|
241
|
+
except (OSError, json.JSONDecodeError):
|
|
242
|
+
return []
|
|
243
|
+
|
|
244
|
+
names = []
|
|
245
|
+
for repo in multi_json.get("repos", []):
|
|
246
|
+
install_sets = repo.get("installSets")
|
|
247
|
+
if install_sets is not None and WORKSPACE_INSTALL_SET not in install_sets:
|
|
248
|
+
continue
|
|
249
|
+
if name := repo.get("name"):
|
|
250
|
+
names.append(name)
|
|
251
|
+
return names
|
|
252
|
+
|
|
253
|
+
|
|
179
254
|
def _multi_sync(ws_path: Path) -> None:
|
|
180
255
|
click.echo(f"Running multi sync --install-set {WORKSPACE_INSTALL_SET}...")
|
|
181
256
|
sync_workspace(ws_path, install_set=WORKSPACE_INSTALL_SET)
|
|
@@ -255,6 +330,23 @@ def _ensure_codex_home_default_files(workspace_dir: str) -> None:
|
|
|
255
330
|
click.echo(f"Created Codex home default at {target_path}")
|
|
256
331
|
|
|
257
332
|
|
|
333
|
+
def _ensure_codex_home_dispatcher_config() -> None:
|
|
334
|
+
"""Create the missing Openbase dispatcher config."""
|
|
335
|
+
if CODEX_DISPATCHER_CONFIG_PATH.exists():
|
|
336
|
+
click.echo(
|
|
337
|
+
f"Codex home dispatcher config already exists at "
|
|
338
|
+
f"{CODEX_DISPATCHER_CONFIG_PATH}"
|
|
339
|
+
)
|
|
340
|
+
return
|
|
341
|
+
|
|
342
|
+
CODEX_DISPATCHER_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
343
|
+
CODEX_DISPATCHER_CONFIG_PATH.write_text(
|
|
344
|
+
json.dumps(CODEX_HOME_DEFAULT_DISPATCHER_CONFIG, indent=2) + "\n",
|
|
345
|
+
encoding="utf-8",
|
|
346
|
+
)
|
|
347
|
+
click.echo(f"Created Codex home dispatcher config at {CODEX_DISPATCHER_CONFIG_PATH}")
|
|
348
|
+
|
|
349
|
+
|
|
258
350
|
def _symlink_codex_home_skills(workspace_dir: str) -> None:
|
|
259
351
|
"""Symlink workspace-owned skills into the Openbase Codex home."""
|
|
260
352
|
source_root = Path(workspace_dir) / CODEX_HOME_SKILLS_SOURCE_DIR
|
|
@@ -284,6 +376,141 @@ def _symlink_codex_home_skills(workspace_dir: str) -> None:
|
|
|
284
376
|
click.echo(f"Linked Codex home skill {target_path} → {source_path}")
|
|
285
377
|
|
|
286
378
|
|
|
379
|
+
def _ensure_codex_home_config(workspace_dir: str) -> None:
|
|
380
|
+
"""Configure Openbase's service Codex home."""
|
|
381
|
+
CODEX_HOME_DIR.mkdir(parents=True, exist_ok=True)
|
|
382
|
+
config_path = CODEX_HOME_DIR / "config.toml"
|
|
383
|
+
command_path, args = _super_agents_mcp_command(Path(workspace_dir))
|
|
384
|
+
block = (
|
|
385
|
+
f"[{SUPER_AGENTS_MCP_TABLE}]\n"
|
|
386
|
+
f"command = {json.dumps(str(command_path))}\n"
|
|
387
|
+
f"{_toml_args_line(args)}"
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
if not command_path.is_file():
|
|
391
|
+
click.echo(
|
|
392
|
+
f"Super Agents MCP command not found at {command_path}; "
|
|
393
|
+
"writing the expected config path anyway."
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
existing = ""
|
|
397
|
+
if config_path.is_file():
|
|
398
|
+
existing = config_path.read_text(encoding="utf-8")
|
|
399
|
+
|
|
400
|
+
updated = _ensure_toml_root_values(existing, CODEX_HOME_PERMISSION_VALUES)
|
|
401
|
+
updated = _replace_toml_table(updated, SUPER_AGENTS_MCP_TABLE, block)
|
|
402
|
+
if updated == existing:
|
|
403
|
+
click.echo(f"Codex home config already configured at {config_path}")
|
|
404
|
+
return
|
|
405
|
+
|
|
406
|
+
config_path.write_text(updated, encoding="utf-8")
|
|
407
|
+
click.echo(f"Configured Codex home config at {config_path}")
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _super_agents_mcp_command(workspace_dir: Path) -> tuple[Path, list[str]]:
|
|
411
|
+
candidates = (
|
|
412
|
+
workspace_dir / ".venv" / "bin" / SUPER_AGENTS_MCP_COMMAND,
|
|
413
|
+
workspace_dir / "cli" / ".venv" / "bin" / SUPER_AGENTS_MCP_COMMAND,
|
|
414
|
+
)
|
|
415
|
+
for candidate in candidates:
|
|
416
|
+
if candidate.is_file():
|
|
417
|
+
return candidate, []
|
|
418
|
+
|
|
419
|
+
if command := which(SUPER_AGENTS_MCP_COMMAND):
|
|
420
|
+
return Path(command), []
|
|
421
|
+
|
|
422
|
+
if uv_bin := which("uv"):
|
|
423
|
+
run_dir = workspace_dir / "cli"
|
|
424
|
+
if not run_dir.is_dir():
|
|
425
|
+
run_dir = workspace_dir
|
|
426
|
+
return Path(uv_bin), [
|
|
427
|
+
"--directory",
|
|
428
|
+
str(run_dir),
|
|
429
|
+
"run",
|
|
430
|
+
SUPER_AGENTS_MCP_COMMAND,
|
|
431
|
+
]
|
|
432
|
+
|
|
433
|
+
return candidates[0], []
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def _toml_args_line(args: list[str]) -> str:
|
|
437
|
+
if not args:
|
|
438
|
+
return ""
|
|
439
|
+
return f"args = {json.dumps(args)}\n"
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _ensure_toml_root_values(
|
|
443
|
+
text: str,
|
|
444
|
+
values: tuple[tuple[str, str], ...],
|
|
445
|
+
) -> str:
|
|
446
|
+
lines = text.splitlines()
|
|
447
|
+
first_table_index = next(
|
|
448
|
+
(
|
|
449
|
+
index
|
|
450
|
+
for index, line in enumerate(lines)
|
|
451
|
+
if line.strip().startswith("[") and line.strip().endswith("]")
|
|
452
|
+
),
|
|
453
|
+
len(lines),
|
|
454
|
+
)
|
|
455
|
+
root_lines = lines[:first_table_index]
|
|
456
|
+
table_lines = lines[first_table_index:]
|
|
457
|
+
keys = {key for key, _value in values}
|
|
458
|
+
updated_root = [
|
|
459
|
+
line
|
|
460
|
+
for line in root_lines
|
|
461
|
+
if _toml_root_key(line) not in keys
|
|
462
|
+
]
|
|
463
|
+
|
|
464
|
+
while updated_root and not updated_root[-1].strip():
|
|
465
|
+
updated_root.pop()
|
|
466
|
+
|
|
467
|
+
for key, value in values:
|
|
468
|
+
updated_root.append(f"{key} = {value}")
|
|
469
|
+
|
|
470
|
+
while table_lines and not table_lines[0].strip():
|
|
471
|
+
table_lines.pop(0)
|
|
472
|
+
|
|
473
|
+
if table_lines:
|
|
474
|
+
return "\n".join(updated_root) + "\n\n" + "\n".join(table_lines) + "\n"
|
|
475
|
+
return "\n".join(updated_root) + "\n"
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def _toml_root_key(line: str) -> str | None:
|
|
479
|
+
stripped = line.strip()
|
|
480
|
+
if not stripped or stripped.startswith("#") or "=" not in stripped:
|
|
481
|
+
return None
|
|
482
|
+
return stripped.split("=", 1)[0].strip()
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _replace_toml_table(text: str, table_name: str, block: str) -> str:
|
|
486
|
+
target_header = f"[{table_name}]"
|
|
487
|
+
lines = text.splitlines()
|
|
488
|
+
output: list[str] = []
|
|
489
|
+
index = 0
|
|
490
|
+
|
|
491
|
+
while index < len(lines):
|
|
492
|
+
if lines[index].strip() == target_header:
|
|
493
|
+
index += 1
|
|
494
|
+
while index < len(lines):
|
|
495
|
+
stripped = lines[index].strip()
|
|
496
|
+
if stripped.startswith("[") and stripped.endswith("]"):
|
|
497
|
+
break
|
|
498
|
+
index += 1
|
|
499
|
+
while output and not output[-1].strip():
|
|
500
|
+
output.pop()
|
|
501
|
+
continue
|
|
502
|
+
|
|
503
|
+
output.append(lines[index])
|
|
504
|
+
index += 1
|
|
505
|
+
|
|
506
|
+
while output and not output[-1].strip():
|
|
507
|
+
output.pop()
|
|
508
|
+
|
|
509
|
+
if output:
|
|
510
|
+
return "\n".join(output) + "\n\n" + block
|
|
511
|
+
return block
|
|
512
|
+
|
|
513
|
+
|
|
287
514
|
def _workspace_skill_sources(source_root: Path) -> list[Path]:
|
|
288
515
|
candidate_roots = [source_root / "skills", source_root]
|
|
289
516
|
seen: set[Path] = set()
|
|
@@ -419,8 +646,6 @@ def _ensure_env_file(
|
|
|
419
646
|
"LIVEKIT_CODEX_THREAD_CWD=~",
|
|
420
647
|
"# Cartesia voice used by the LiveKit agent TTS.",
|
|
421
648
|
"CARTESIA_VOICE_ID=9626c31c-bec5-4cca-baa8-f8ba9e84c8bc",
|
|
422
|
-
"# Optional comma-separated voices for direct thread routing: voice-id:Display Name.",
|
|
423
|
-
"# CARTESIA_SUPER_AGENT_VOICES=f786b574-daa5-4673-aa0c-cbe3e8534c02:Alice",
|
|
424
649
|
"OPENBASE_CODER_CLI_OAUTH_CLIENT_ID=openbase-coder-cli",
|
|
425
650
|
]
|
|
426
651
|
|
|
@@ -42,6 +42,15 @@ from livekit.agents.types import DEFAULT_API_CONNECT_OPTIONS, NOT_GIVEN
|
|
|
42
42
|
from livekit.plugins import assemblyai, cartesia, deepgram, silero
|
|
43
43
|
from livekit.plugins.turn_detector.multilingual import MultilingualModel
|
|
44
44
|
|
|
45
|
+
from openbase_coder_cli.brain_score import (
|
|
46
|
+
brain_score_token_configured,
|
|
47
|
+
brain_score_token_file,
|
|
48
|
+
load_brain_score_token,
|
|
49
|
+
)
|
|
50
|
+
from openbase_coder_cli.cartesia_voice_catalog import (
|
|
51
|
+
DEFAULT_SUPER_AGENT_VOICE_IDS,
|
|
52
|
+
cartesia_voice_for_id,
|
|
53
|
+
)
|
|
45
54
|
from openbase_coder_cli.dispatcher_config import dispatcher_voice
|
|
46
55
|
from openbase_coder_cli.livekit_agent.codex_app_client import CodexAppServerClient
|
|
47
56
|
from openbase_coder_cli.livekit_agent.speech_formatter import format_for_speech
|
|
@@ -165,12 +174,7 @@ BRAIN_SCORE_OUTPUT_PATH = Path(
|
|
|
165
174
|
str(Path.home() / ".openbase" / "brain_score.json"),
|
|
166
175
|
)
|
|
167
176
|
).expanduser()
|
|
168
|
-
BRAIN_SCORE_TOKEN_FILE =
|
|
169
|
-
os.getenv(
|
|
170
|
-
"OPENBASE_BRAIN_SCORE_TOKEN_FILE",
|
|
171
|
-
str(Path.home() / ".openbase" / "brain_score_token"),
|
|
172
|
-
)
|
|
173
|
-
).expanduser()
|
|
177
|
+
BRAIN_SCORE_TOKEN_FILE = brain_score_token_file()
|
|
174
178
|
BRAIN_SCORE_LATITUDE = os.getenv("OPENBASE_BRAIN_SCORE_LATITUDE", "").strip()
|
|
175
179
|
BRAIN_SCORE_LONGITUDE = os.getenv("OPENBASE_BRAIN_SCORE_LONGITUDE", "").strip()
|
|
176
180
|
|
|
@@ -181,27 +185,6 @@ class CartesiaVoice:
|
|
|
181
185
|
name: str
|
|
182
186
|
|
|
183
187
|
|
|
184
|
-
def _super_agent_voices(env: Mapping[str, str]) -> tuple[CartesiaVoice, ...]:
|
|
185
|
-
named_configured = env.get("CARTESIA_SUPER_AGENT_VOICES")
|
|
186
|
-
if named_configured is not None:
|
|
187
|
-
return _parse_voices(named_configured)
|
|
188
|
-
|
|
189
|
-
configured = env.get("CARTESIA_SUPER_AGENT_VOICE_IDS")
|
|
190
|
-
if configured is not None:
|
|
191
|
-
return _voices_from_ids(_parse_voice_ids(configured))
|
|
192
|
-
|
|
193
|
-
dispatcher_voice_id = env.get("CARTESIA_VOICE_ID", DEFAULT_CARTESIA_VOICE_ID)
|
|
194
|
-
announcer_voice_id = env.get(
|
|
195
|
-
"CARTESIA_ANNOUNCER_VOICE_ID",
|
|
196
|
-
DEFAULT_CARTESIA_ANNOUNCER_VOICE_ID,
|
|
197
|
-
)
|
|
198
|
-
return _voices_from_ids(
|
|
199
|
-
voice_id
|
|
200
|
-
for voice_id in (announcer_voice_id,)
|
|
201
|
-
if voice_id and voice_id != dispatcher_voice_id
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
|
|
205
188
|
def dispatcher_voice_config(
|
|
206
189
|
*,
|
|
207
190
|
config_path: str | Path | None = None,
|
|
@@ -212,49 +195,27 @@ def dispatcher_voice_config(
|
|
|
212
195
|
return CartesiaVoice(voice_id=configured["id"], name=configured["name"])
|
|
213
196
|
|
|
214
197
|
|
|
215
|
-
def _parse_voice_ids(value: str) -> tuple[str, ...]:
|
|
216
|
-
return tuple(
|
|
217
|
-
voice_id for voice_id in (part.strip() for part in value.split(",")) if voice_id
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
def _parse_voices(value: str) -> tuple[CartesiaVoice, ...]:
|
|
222
|
-
voices: list[CartesiaVoice] = []
|
|
223
|
-
for part in (part.strip() for part in value.split(",")):
|
|
224
|
-
if not part:
|
|
225
|
-
continue
|
|
226
|
-
voice_id, separator, name = part.partition(":")
|
|
227
|
-
trimmed_voice_id = voice_id.strip()
|
|
228
|
-
if not trimmed_voice_id:
|
|
229
|
-
continue
|
|
230
|
-
trimmed_name = name.strip() if separator else ""
|
|
231
|
-
voices.append(
|
|
232
|
-
CartesiaVoice(
|
|
233
|
-
voice_id=trimmed_voice_id,
|
|
234
|
-
name=trimmed_name or f"Voice {len(voices) + 1}",
|
|
235
|
-
)
|
|
236
|
-
)
|
|
237
|
-
return tuple(voices)
|
|
238
|
-
|
|
239
|
-
|
|
240
198
|
def _voices_from_ids(voice_ids) -> tuple[CartesiaVoice, ...]:
|
|
241
199
|
return tuple(
|
|
242
|
-
CartesiaVoice(
|
|
200
|
+
CartesiaVoice(
|
|
201
|
+
voice_id=voice_id,
|
|
202
|
+
name=cartesia_voice_for_id(voice_id).name
|
|
203
|
+
if cartesia_voice_for_id(voice_id)
|
|
204
|
+
else f"Voice {index + 1}",
|
|
205
|
+
)
|
|
243
206
|
for index, voice_id in enumerate(voice_ids)
|
|
244
207
|
)
|
|
245
208
|
|
|
246
209
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
voice.voice_id for voice in CARTESIA_SUPER_AGENT_VOICES
|
|
250
|
-
)
|
|
210
|
+
SUPER_AGENT_VOICE_IDS = DEFAULT_SUPER_AGENT_VOICE_IDS
|
|
211
|
+
SUPER_AGENT_VOICES = _voices_from_ids(SUPER_AGENT_VOICE_IDS)
|
|
251
212
|
|
|
252
213
|
|
|
253
214
|
def _current_super_agent_voices() -> tuple[CartesiaVoice, ...]:
|
|
254
|
-
voice_ids = tuple(voice.voice_id for voice in
|
|
255
|
-
if voice_ids == tuple(
|
|
256
|
-
return
|
|
257
|
-
return _voices_from_ids(
|
|
215
|
+
voice_ids = tuple(voice.voice_id for voice in SUPER_AGENT_VOICES)
|
|
216
|
+
if voice_ids == tuple(SUPER_AGENT_VOICE_IDS):
|
|
217
|
+
return SUPER_AGENT_VOICES
|
|
218
|
+
return _voices_from_ids(SUPER_AGENT_VOICE_IDS)
|
|
258
219
|
|
|
259
220
|
|
|
260
221
|
def _normalize_spoken_command(text: str) -> str:
|
|
@@ -507,20 +468,11 @@ def _event_text_hash(text: str) -> str:
|
|
|
507
468
|
|
|
508
469
|
|
|
509
470
|
def _load_brain_score_token() -> str:
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
except FileNotFoundError:
|
|
516
|
-
return ""
|
|
517
|
-
except OSError:
|
|
518
|
-
logger.warning(
|
|
519
|
-
"Unable to read brain score token file %s",
|
|
520
|
-
BRAIN_SCORE_TOKEN_FILE,
|
|
521
|
-
exc_info=True,
|
|
522
|
-
)
|
|
523
|
-
return ""
|
|
471
|
+
return load_brain_score_token()
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def _brain_score_enabled() -> bool:
|
|
475
|
+
return BRAIN_SCORE_ENABLED and brain_score_token_configured()
|
|
524
476
|
|
|
525
477
|
|
|
526
478
|
class BrainScoreSTT(livekit_stt.STT):
|
|
@@ -2559,12 +2511,12 @@ def _build_stt():
|
|
|
2559
2511
|
if LIVEKIT_STT_PROVIDER == "deepgram":
|
|
2560
2512
|
logger.info("Using Deepgram STT")
|
|
2561
2513
|
stt = deepgram.STT(api_key=DEEPGRAM_API_KEY)
|
|
2562
|
-
stt = BrainScoreSTT(stt) if
|
|
2514
|
+
stt = BrainScoreSTT(stt) if _brain_score_enabled() else stt
|
|
2563
2515
|
return LoggingSTT(stt) if LIVEKIT_VERBOSE_LOGGING else stt
|
|
2564
2516
|
if LIVEKIT_STT_PROVIDER == "assemblyai":
|
|
2565
2517
|
logger.info("Using AssemblyAI STT")
|
|
2566
2518
|
stt = assemblyai.STT(api_key=ASSEMBLY_AI_API_KEY)
|
|
2567
|
-
stt = BrainScoreSTT(stt) if
|
|
2519
|
+
stt = BrainScoreSTT(stt) if _brain_score_enabled() else stt
|
|
2568
2520
|
return LoggingSTT(stt) if LIVEKIT_VERBOSE_LOGGING else stt
|
|
2569
2521
|
|
|
2570
2522
|
raise ValueError(f"Unsupported LIVEKIT_STT_PROVIDER={LIVEKIT_STT_PROVIDER!r}")
|
|
@@ -6,13 +6,13 @@ import json
|
|
|
6
6
|
import os
|
|
7
7
|
import time
|
|
8
8
|
import uuid
|
|
9
|
-
from collections.abc import Mapping
|
|
10
9
|
from dataclasses import asdict, dataclass
|
|
11
10
|
from pathlib import Path
|
|
12
11
|
|
|
13
12
|
import livekit.api as livekit_api
|
|
14
13
|
|
|
15
14
|
from openbase_coder_cli.cartesia_voice_catalog import (
|
|
15
|
+
DEFAULT_SUPER_AGENT_VOICE_IDS,
|
|
16
16
|
cartesia_voice_for_id,
|
|
17
17
|
cartesia_voice_for_name,
|
|
18
18
|
)
|
|
@@ -120,52 +120,6 @@ def instruction_override_supported() -> bool:
|
|
|
120
120
|
return True
|
|
121
121
|
|
|
122
122
|
|
|
123
|
-
def _super_agent_voices(env: Mapping[str, str]) -> tuple[CartesiaVoice, ...]:
|
|
124
|
-
named_configured = env.get("CARTESIA_SUPER_AGENT_VOICES")
|
|
125
|
-
if named_configured is not None:
|
|
126
|
-
return _parse_voices(named_configured)
|
|
127
|
-
|
|
128
|
-
configured = env.get("CARTESIA_SUPER_AGENT_VOICE_IDS")
|
|
129
|
-
if configured is not None:
|
|
130
|
-
return _voices_from_ids(_parse_voice_ids(configured))
|
|
131
|
-
|
|
132
|
-
dispatcher_voice_id = env.get("CARTESIA_VOICE_ID", DEFAULT_CARTESIA_VOICE_ID)
|
|
133
|
-
announcer_voice_id = env.get(
|
|
134
|
-
"CARTESIA_ANNOUNCER_VOICE_ID",
|
|
135
|
-
DEFAULT_CARTESIA_ANNOUNCER_VOICE_ID,
|
|
136
|
-
)
|
|
137
|
-
return _voices_from_ids(
|
|
138
|
-
voice_id
|
|
139
|
-
for voice_id in (announcer_voice_id,)
|
|
140
|
-
if voice_id and voice_id != dispatcher_voice_id
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def _parse_voice_ids(value: str) -> tuple[str, ...]:
|
|
145
|
-
return tuple(
|
|
146
|
-
voice_id for voice_id in (part.strip() for part in value.split(",")) if voice_id
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def _parse_voices(value: str) -> tuple[CartesiaVoice, ...]:
|
|
151
|
-
voices: list[CartesiaVoice] = []
|
|
152
|
-
for part in (part.strip() for part in value.split(",")):
|
|
153
|
-
if not part:
|
|
154
|
-
continue
|
|
155
|
-
voice_id, separator, name = part.partition(":")
|
|
156
|
-
trimmed_voice_id = voice_id.strip()
|
|
157
|
-
if not trimmed_voice_id:
|
|
158
|
-
continue
|
|
159
|
-
trimmed_name = name.strip() if separator else ""
|
|
160
|
-
voices.append(
|
|
161
|
-
CartesiaVoice(
|
|
162
|
-
voice_id=trimmed_voice_id,
|
|
163
|
-
name=trimmed_name or f"Voice {len(voices) + 1}",
|
|
164
|
-
)
|
|
165
|
-
)
|
|
166
|
-
return tuple(voices)
|
|
167
|
-
|
|
168
|
-
|
|
169
123
|
def _voices_from_ids(voice_ids) -> tuple[CartesiaVoice, ...]:
|
|
170
124
|
return tuple(
|
|
171
125
|
CartesiaVoice(
|
|
@@ -178,17 +132,15 @@ def _voices_from_ids(voice_ids) -> tuple[CartesiaVoice, ...]:
|
|
|
178
132
|
)
|
|
179
133
|
|
|
180
134
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
voice.voice_id for voice in CARTESIA_SUPER_AGENT_VOICES
|
|
184
|
-
)
|
|
135
|
+
SUPER_AGENT_VOICE_IDS = DEFAULT_SUPER_AGENT_VOICE_IDS
|
|
136
|
+
SUPER_AGENT_VOICES = _voices_from_ids(SUPER_AGENT_VOICE_IDS)
|
|
185
137
|
|
|
186
138
|
|
|
187
139
|
def _current_super_agent_voices() -> tuple[CartesiaVoice, ...]:
|
|
188
|
-
voice_ids = tuple(voice.voice_id for voice in
|
|
189
|
-
if voice_ids == tuple(
|
|
190
|
-
return
|
|
191
|
-
return _voices_from_ids(
|
|
140
|
+
voice_ids = tuple(voice.voice_id for voice in SUPER_AGENT_VOICES)
|
|
141
|
+
if voice_ids == tuple(SUPER_AGENT_VOICE_IDS):
|
|
142
|
+
return SUPER_AGENT_VOICES
|
|
143
|
+
return _voices_from_ids(SUPER_AGENT_VOICE_IDS)
|
|
192
144
|
|
|
193
145
|
|
|
194
146
|
def stable_super_agent_voice(
|
|
@@ -9,6 +9,8 @@ from typing import Any
|
|
|
9
9
|
from rest_framework.decorators import api_view
|
|
10
10
|
from rest_framework.response import Response
|
|
11
11
|
|
|
12
|
+
from openbase_coder_cli.brain_score import brain_score_token_configured
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
def default_brain_score_output_path() -> Path:
|
|
14
16
|
return Path(
|
|
@@ -54,19 +56,27 @@ def _read_brain_score_file(path: Path) -> dict[str, Any] | None:
|
|
|
54
56
|
return payload
|
|
55
57
|
|
|
56
58
|
|
|
59
|
+
def _unavailable_response(*, disabled_reason: str | None = None) -> dict[str, Any]:
|
|
60
|
+
return {
|
|
61
|
+
"available": False,
|
|
62
|
+
"brain_readiness_score": None,
|
|
63
|
+
"brs": None,
|
|
64
|
+
"parallel_voice_threshold": None,
|
|
65
|
+
"updated_at": None,
|
|
66
|
+
"computed_at": None,
|
|
67
|
+
"chunk_index": None,
|
|
68
|
+
"age_seconds": None,
|
|
69
|
+
"disabled_reason": disabled_reason,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
57
73
|
def build_brain_readiness_response(path: Path | None = None) -> dict[str, Any]:
|
|
74
|
+
if not brain_score_token_configured():
|
|
75
|
+
return _unavailable_response(disabled_reason="missing_token")
|
|
76
|
+
|
|
58
77
|
payload = _read_brain_score_file(path or default_brain_score_output_path())
|
|
59
78
|
if payload is None:
|
|
60
|
-
return
|
|
61
|
-
"available": False,
|
|
62
|
-
"brain_readiness_score": None,
|
|
63
|
-
"brs": None,
|
|
64
|
-
"parallel_voice_threshold": None,
|
|
65
|
-
"updated_at": None,
|
|
66
|
-
"computed_at": None,
|
|
67
|
-
"chunk_index": None,
|
|
68
|
-
"age_seconds": None,
|
|
69
|
-
}
|
|
79
|
+
return _unavailable_response()
|
|
70
80
|
|
|
71
81
|
score = _coerce_score(payload.get("brs"))
|
|
72
82
|
updated_at = payload.get("updated_at")
|
|
@@ -83,6 +93,7 @@ def build_brain_readiness_response(path: Path | None = None) -> dict[str, Any]:
|
|
|
83
93
|
"computed_at": payload.get("computed_at"),
|
|
84
94
|
"chunk_index": payload.get("chunk_index"),
|
|
85
95
|
"age_seconds": age_seconds,
|
|
96
|
+
"disabled_reason": None,
|
|
86
97
|
}
|
|
87
98
|
|
|
88
99
|
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/codex_app_client.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/codex_thread_state.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/codex_transport.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/codex_turns.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/livekit_agent/speech_formatter.py
RENAMED
|
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
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/__init__.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/agents_md.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/approvals.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/apps.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/auth.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/common.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/consumers.py
RENAMED
|
File without changes
|
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/livekit.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/models.py
RENAMED
|
File without changes
|
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/projects.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/reports.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/routines.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/routing.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/skills.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/threads.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/urls.py
RENAMED
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/openbase_coder_cli_app/views.py
RENAMED
|
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
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/console_settings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/launchctl_tools.py
RENAMED
|
File without changes
|
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/openbase_services.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openbase_coder-0.1.3 → openbase_coder-0.1.5}/openbase_coder_cli/services/tailnet_devices.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|