Skip to content

Commit e396234

Browse files
authored
Merge pull request #55 from zhujian0805/main
Migrate Droid BYOK config to settings.json customModels format
2 parents 9e9622f + cfe5573 commit e396234

File tree

4 files changed

+42
-18
lines changed

4 files changed

+42
-18
lines changed

code_assistant_manager/tools.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ tools:
214214
injected: []
215215
filesystem:
216216
generated:
217-
- "~/.factory/settings.json containing custom_models entries with base_url, api_key, provider, and max_tokens"
217+
- "~/.factory/settings.json containing customModels entries with displayName, model, baseUrl, apiKey, provider, and maxOutputTokens"
218218

219219
iflow:
220220
enabled: true

code_assistant_manager/tools/droid.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,25 @@ def _process_endpoint(self, endpoint_name: str) -> Optional[List[str]]:
7272
return None
7373

7474
# Build entries for each selected model
75+
api_key_env = str(endpoint_config.get("api_key_env", "")).strip()
76+
api_key_value = (
77+
f"${{{api_key_env}}}" if api_key_env else endpoint_config["actual_api_key"]
78+
)
7579
entries = []
7680
for model in selected_models:
7781
display_name = f"{model} [{endpoint_name}]"
78-
entry = f"{display_name}|{endpoint_config['endpoint']}|{endpoint_config['actual_api_key']}|generic-chat-completion-api|65536"
82+
entry = f"{display_name}|{endpoint_config['endpoint']}|{api_key_value}|generic-chat-completion-api|65536"
7983
entries.append(entry)
8084

8185
return entries
8286

8387
def _write_droid_settings(self, selected_entries: List[str]) -> Path:
84-
"""Persist Droid custom models to ~/.factory/config.json."""
88+
"""Persist Droid custom models to ~/.factory/settings.json."""
8589
config_dir = Path.home() / ".factory"
86-
config_file = config_dir / "config.json"
90+
config_file = config_dir / "settings.json"
8791
config_dir.mkdir(parents=True, exist_ok=True)
8892

89-
# Preserve existing settings (plugins, etc) and only update custom_models.
93+
# Preserve existing settings (plugins, etc) and only update customModels.
9094
settings: dict = {}
9195
if config_file.exists():
9296
try:
@@ -98,7 +102,7 @@ def _write_droid_settings(self, selected_entries: List[str]) -> Path:
98102
json_models = self._build_models_json(selected_entries)
99103

100104
# Canonical location for Droid BYOK custom models.
101-
settings["custom_models"] = json_models
105+
settings["customModels"] = json_models
102106

103107
with open(config_file, "w", encoding="utf-8") as f:
104108
json.dump(settings, f, indent=2)
@@ -131,7 +135,7 @@ def run(self, args: List[str] = None) -> int:
131135
- Loads environment variables
132136
- Ensures required commands are available
133137
- Aggregates selected models from configured endpoints
134-
- Writes custom_models to ~/.factory/config.json and runs the `droid` CLI
138+
- Writes customModels to ~/.factory/settings.json and runs the `droid` CLI
135139
136140
Args:
137141
args: List of arguments to pass to the Droid CLI
@@ -178,7 +182,7 @@ def run(self, args: List[str] = None) -> int:
178182

179183
print(f"Total models selected: {len(selected_entries)}\n")
180184

181-
# Persist Droid custom models to ~/.factory/config.json
185+
# Persist Droid custom models to ~/.factory/settings.json
182186
settings_file = self._write_droid_settings(selected_entries)
183187
print(f"Droid settings written to {settings_file}\n")
184188

@@ -201,26 +205,26 @@ def run(self, args: List[str] = None) -> int:
201205
return self._run_tool_with_env(command, env, "droid", interactive=True)
202206

203207
def _build_models_json(self, entries: List[str]) -> List[dict]:
204-
"""Build models JSON from pipe-delimited entries (compat with legacy implementation)."""
208+
"""Build models JSON from pipe-delimited entries for Droid settings.json."""
205209
models: List[dict] = []
206210
for entry in entries:
207211
parts = entry.split("|")
208212
if len(parts) < 5:
209213
continue
210-
display, base_url, api_key, _provider, max_tokens = parts[:5]
214+
display, base_url, api_key, provider, max_tokens = parts[:5]
211215
model_id = display.split("[")[0].strip()
212216
try:
213217
max_tokens_val = int(max_tokens)
214218
except ValueError:
215219
max_tokens_val = 0
216220
models.append(
217221
{
218-
"model_display_name": display,
222+
"displayName": display,
219223
"model": model_id,
220-
"base_url": base_url,
221-
"api_key": api_key,
222-
"provider": "generic-chat-completion-api",
223-
"max_tokens": max_tokens_val,
224+
"baseUrl": base_url,
225+
"apiKey": api_key,
226+
"provider": provider or "generic-chat-completion-api",
227+
"maxOutputTokens": max_tokens_val,
224228
}
225229
)
226230
return models

docs/TOOLS_IMPLEMENTATION_COMPLETE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The Code Assistant Manager Python package now has complete implementations for a
4040
- Optional upgrade to latest version
4141
- Multi-endpoint model configuration
4242
- Generates ~/.factory/settings.json with selected models
43-
- JSON format: custom_models array with model display names, base URLs, API keys, providers, max tokens
43+
- JSON format: customModels array with displayName, model, baseUrl, apiKey, provider, maxOutputTokens
4444
- Per-endpoint model selection (with skip option)
4545

4646
#### 6. **CopilotTool**

tests/test_tools.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,28 @@ def test_droid_build_models_json(self, config_manager):
192192
models = tool._build_models_json(entries)
193193
assert len(models) == 2
194194
assert models[0]["model"] == "model1"
195-
assert models[0]["base_url"] == "https://api.com"
196-
assert models[1]["max_tokens"] == 65536
195+
assert models[0]["baseUrl"] == "https://api.com"
196+
assert models[1]["maxOutputTokens"] == 65536
197+
198+
@patch.dict(os.environ, {"CODE_ASSISTANT_MANAGER_NONINTERACTIVE": "1"})
199+
def test_droid_process_endpoint_prefers_api_key_env_reference(self, config_manager):
200+
"""Test Droid entries use ${API_KEY_ENV} in settings.json-compatible output when available."""
201+
tool = DroidTool(config_manager)
202+
tool.endpoint_manager = MagicMock()
203+
tool.endpoint_manager.get_endpoint_config.return_value = (
204+
True,
205+
{
206+
"endpoint": "https://api.example.com",
207+
"actual_api_key": "raw-key",
208+
"api_key_env": "FACTORY_TEST_KEY",
209+
},
210+
)
211+
tool.endpoint_manager.fetch_models.return_value = (True, ["model1"])
212+
213+
entries = tool._process_endpoint("endpoint1")
214+
assert entries is not None
215+
assert len(entries) == 1
216+
assert "${FACTORY_TEST_KEY}" in entries[0]
197217

198218
@patch("code_assistant_manager.tools.subprocess.run")
199219
@patch("code_assistant_manager.tools.select_model")

0 commit comments

Comments
 (0)