discolike-cli 0.6.1__tar.gz → 0.6.2__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.
Files changed (48) hide show
  1. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/PKG-INFO +3 -2
  2. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/pyproject.toml +3 -2
  3. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/providers.py +51 -6
  4. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_discogen_cli.py +4 -1
  5. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_providers_cli.py +65 -0
  6. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/.gitignore +0 -0
  7. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/LICENSE +0 -0
  8. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/README.md +0 -0
  9. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/__init__.py +0 -0
  10. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/_help.py +0 -0
  11. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/_inputs.py +0 -0
  12. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/_loopback.py +0 -0
  13. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/_output.py +0 -0
  14. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/account.py +0 -0
  15. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/auth.py +0 -0
  16. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/bulk.py +0 -0
  17. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/company.py +0 -0
  18. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/contacts.py +0 -0
  19. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/discogen.py +0 -0
  20. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/discover.py +0 -0
  21. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/email.py +0 -0
  22. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/enrich.py +0 -0
  23. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/main.py +0 -0
  24. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/match.py +0 -0
  25. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/prospecting.py +0 -0
  26. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/py.typed +0 -0
  27. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/queries.py +0 -0
  28. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/src/discolike_cli/signup.py +0 -0
  29. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/conftest.py +0 -0
  30. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_account.py +0 -0
  31. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_auth.py +0 -0
  32. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_auth_oauth.py +0 -0
  33. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_bulk_cli.py +0 -0
  34. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_company.py +0 -0
  35. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_contacts_cli.py +0 -0
  36. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_discover.py +0 -0
  37. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_email_cli.py +0 -0
  38. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_enrich_cli.py +0 -0
  39. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_entrypoint.py +0 -0
  40. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_file_inputs_cli.py +0 -0
  41. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_help.py +0 -0
  42. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_inputs.py +0 -0
  43. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_match_cli.py +0 -0
  44. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_output.py +0 -0
  45. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_prospecting_cli.py +0 -0
  46. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_queries_cli.py +0 -0
  47. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_sdk_parity.py +0 -0
  48. {discolike_cli-0.6.1 → discolike_cli-0.6.2}/tests/test_signup_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: discolike-cli
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: Official CLI for the DiscoLike API
5
5
  Project-URL: Homepage, https://www.discolike.com
6
6
  Project-URL: Documentation, https://docs.discolike.com
@@ -21,10 +21,11 @@ Classifier: Programming Language :: Python :: 3.11
21
21
  Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Programming Language :: Python :: 3.13
23
23
  Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Programming Language :: Python :: 3.15
24
25
  Classifier: Topic :: Utilities
25
26
  Classifier: Typing :: Typed
26
27
  Requires-Python: >=3.11
27
- Requires-Dist: discolike==0.6.1
28
+ Requires-Dist: discolike==0.6.2
28
29
  Requires-Dist: rich>=13.0
29
30
  Requires-Dist: typer<0.28,>=0.26.1
30
31
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "discolike-cli"
7
- version = "0.6.1"
7
+ version = "0.6.2"
8
8
  description = "Official CLI for the DiscoLike API"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -12,7 +12,7 @@ license-files = ["LICENSE"]
12
12
  requires-python = ">=3.11"
13
13
  authors = [{ name = "DiscoLike", email = "support@discolike.com" }]
14
14
  dependencies = [
15
- "discolike==0.6.1",
15
+ "discolike==0.6.2",
16
16
  "typer>=0.26.1,<0.28",
17
17
  "rich>=13.0",
18
18
  ]
@@ -25,6 +25,7 @@ classifiers = [
25
25
  "Programming Language :: Python :: 3.12",
26
26
  "Programming Language :: Python :: 3.13",
27
27
  "Programming Language :: Python :: 3.14",
28
+ "Programming Language :: Python :: 3.15",
28
29
  "Programming Language :: Python :: 3 :: Only",
29
30
  "Operating System :: OS Independent",
30
31
  "Environment :: Console",
@@ -1,5 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from urllib.parse import parse_qsl
4
+ from urllib.parse import urlencode
5
+
3
6
  import typer
4
7
 
5
8
  from discolike.requests import LLMProviderCreateRequest
@@ -15,6 +18,23 @@ FORMAT_HELP = "Output format: json or table (table auto-selected on a TTY; falls
15
18
  search_providers_app = typer.Typer(help="Manage BYOK web search provider integrations (Tavily, Serper, etc.).")
16
19
  llm_providers_app = typer.Typer(help="Manage BYOK LLM provider integrations (OpenAI, Anthropic, custom endpoints).")
17
20
 
21
+ LLM_PROVIDER_HELP = "LLM provider key, e.g. openai, anthropic, bedrock, snowflake, watsonx, or custom."
22
+ REGION_HELP = (
23
+ "Provider region: an AWS region for bedrock and bedrock_mantle, a watsonx region (us-south, eu-de, ...) "
24
+ "for watsonx. `discogen models` lists them under provider_regions."
25
+ )
26
+ ACCOUNT_HELP = "Snowflake account identifier, e.g. orgname-accountname (snowflake only)."
27
+ PROJECT_HELP = "watsonx project ID (watsonx only)."
28
+
29
+
30
+ def _with_model_settings(model_name: str, *, region: str | None, account: str | None, project: str | None) -> str:
31
+ """The API reads provider settings from model_name after "#", e.g. "<model>#region=us-east-1"."""
32
+ flags = {key: value for key, value in (("region", region), ("account", account), ("project", project)) if value}
33
+ if not flags:
34
+ return model_name
35
+ base, _, existing = model_name.partition("#")
36
+ return f"{base}#{urlencode(dict(parse_qsl(existing)) | flags)}"
37
+
18
38
 
19
39
  @search_providers_app.command("list")
20
40
  @handle_errors
@@ -149,10 +169,13 @@ def llm_list_command(
149
169
  def llm_create_command(
150
170
  ctx: typer.Context,
151
171
  name: str = typer.Option(..., "--name", help="User-friendly name for the integration."),
152
- provider: str = typer.Option(..., "--provider", help="LLM provider name: openai, anthropic, or custom."),
172
+ provider: str = typer.Option(..., "--provider", help=LLM_PROVIDER_HELP),
153
173
  api_key: str = typer.Option(..., "--api-key", help="API key for the provider."),
154
174
  model_name: str = typer.Option(..., "--model-name", help="Default model name for this integration."),
155
175
  base_url: str | None = typer.Option(None, "--base-url", help="Endpoint URL (required for the custom provider)."),
176
+ region: str | None = typer.Option(None, "--region", help=REGION_HELP),
177
+ account: str | None = typer.Option(None, "--account", help=ACCOUNT_HELP),
178
+ project: str | None = typer.Option(None, "--project", help=PROJECT_HELP),
156
179
  ) -> None:
157
180
  """Create an LLM provider integration."""
158
181
  from discolike_cli.main import get_client
@@ -160,7 +183,12 @@ def llm_create_command(
160
183
  request = build_request(
161
184
  LLMProviderCreateRequest,
162
185
  _merge_params(
163
- None, integration_name=name, provider=provider, api_key=api_key, model_name=model_name, base_url=base_url
186
+ None,
187
+ integration_name=name,
188
+ provider=provider,
189
+ api_key=api_key,
190
+ model_name=_with_model_settings(model_name, region=region, account=account, project=project),
191
+ base_url=base_url,
164
192
  ),
165
193
  )
166
194
  emit(get_client(ctx).llm_providers.create(request))
@@ -185,10 +213,13 @@ def llm_update_command(
185
213
  ctx: typer.Context,
186
214
  integration_id: str = typer.Argument(..., help="ID of the LLM provider integration to update."),
187
215
  name: str = typer.Option(..., "--name", help="User-friendly name for the integration."),
188
- provider: str = typer.Option(..., "--provider", help="LLM provider name: openai, anthropic, or custom."),
216
+ provider: str = typer.Option(..., "--provider", help=LLM_PROVIDER_HELP),
189
217
  model_name: str = typer.Option(..., "--model-name", help="Default model name for this integration."),
190
218
  api_key: str | None = typer.Option(None, "--api-key", help="New API key (omit to keep the stored key)."),
191
219
  base_url: str | None = typer.Option(None, "--base-url", help="Endpoint URL (required for the custom provider)."),
220
+ region: str | None = typer.Option(None, "--region", help=REGION_HELP),
221
+ account: str | None = typer.Option(None, "--account", help=ACCOUNT_HELP),
222
+ project: str | None = typer.Option(None, "--project", help=PROJECT_HELP),
192
223
  ) -> None:
193
224
  """Update an LLM provider integration."""
194
225
  from discolike_cli.main import get_client
@@ -196,7 +227,13 @@ def llm_update_command(
196
227
  request = build_request(
197
228
  LLMProviderUpdateRequest,
198
229
  {
199
- **_merge_params(None, integration_name=name, provider=provider, model_name=model_name, base_url=base_url),
230
+ **_merge_params(
231
+ None,
232
+ integration_name=name,
233
+ provider=provider,
234
+ model_name=_with_model_settings(model_name, region=region, account=account, project=project),
235
+ base_url=base_url,
236
+ ),
200
237
  "api_key": api_key,
201
238
  },
202
239
  )
@@ -233,10 +270,13 @@ def llm_set_default_command(
233
270
  def llm_test_connection_command(
234
271
  ctx: typer.Context,
235
272
  name: str = typer.Option("cli-test", "--name", help="Name to label the test configuration."),
236
- provider: str = typer.Option(..., "--provider", help="LLM provider name: openai, anthropic, or custom."),
273
+ provider: str = typer.Option(..., "--provider", help=LLM_PROVIDER_HELP),
237
274
  api_key: str = typer.Option(..., "--api-key", help="API key for the provider."),
238
275
  model_name: str = typer.Option(..., "--model-name", help="Model name to test against."),
239
276
  base_url: str | None = typer.Option(None, "--base-url", help="Endpoint URL (required for the custom provider)."),
277
+ region: str | None = typer.Option(None, "--region", help=REGION_HELP),
278
+ account: str | None = typer.Option(None, "--account", help=ACCOUNT_HELP),
279
+ project: str | None = typer.Option(None, "--project", help=PROJECT_HELP),
240
280
  ) -> None:
241
281
  """Test a provider configuration before saving it."""
242
282
  from discolike_cli.main import get_client
@@ -244,7 +284,12 @@ def llm_test_connection_command(
244
284
  request = build_request(
245
285
  LLMProviderCreateRequest,
246
286
  _merge_params(
247
- None, integration_name=name, provider=provider, api_key=api_key, model_name=model_name, base_url=base_url
287
+ None,
288
+ integration_name=name,
289
+ provider=provider,
290
+ api_key=api_key,
291
+ model_name=_with_model_settings(model_name, region=region, account=account, project=project),
292
+ base_url=base_url,
248
293
  ),
249
294
  )
250
295
  emit(get_client(ctx).llm_providers.test_connection(request))
@@ -314,7 +314,10 @@ def test_discogen_models_hits_models_endpoint(install_build_client: Callable[[Ha
314
314
  install_build_client(handler)
315
315
  result = runner.invoke(app, ["discogen", "models"])
316
316
  assert result.exit_code == 0, result.output
317
- assert json.loads(result.stdout) == {"models": {"openai": [{"name": "gpt-5.4", "supports_web_search": False}]}}
317
+ assert json.loads(result.stdout) == {
318
+ "models": {"openai": [{"name": "gpt-5.4", "supports_web_search": False}]},
319
+ "provider_regions": {},
320
+ }
318
321
 
319
322
 
320
323
  def test_discogen_status_default_family_discogen(install_build_client: Callable[[Handler], None]) -> None:
@@ -138,6 +138,71 @@ def test_llm_providers_create_posts_json(install_build_client: Callable[[Handler
138
138
  }
139
139
 
140
140
 
141
+ def test_llm_providers_create_appends_provider_settings_to_model_name(
142
+ install_build_client: Callable[[Handler], None],
143
+ ) -> None:
144
+ captured: dict[str, dict[str, str]] = {}
145
+
146
+ def handler(request: httpx2.Request) -> httpx2.Response:
147
+ captured["body"] = json.loads(request.content)
148
+ return httpx2.Response(200, json={"message": "created", "integration_id": "llm1"})
149
+
150
+ install_build_client(handler)
151
+ result = runner.invoke(
152
+ app,
153
+ [
154
+ "llm-providers",
155
+ "create",
156
+ "--name",
157
+ "watsonx",
158
+ "--provider",
159
+ "watsonx",
160
+ "--api-key",
161
+ "ibm-key",
162
+ "--model-name",
163
+ "watsonx/ibm/granite-3-8b-instruct",
164
+ "--region",
165
+ "eu-de",
166
+ "--project",
167
+ "11111111-2222-3333-4444-555555555555",
168
+ ],
169
+ )
170
+ assert result.exit_code == 0, result.output
171
+ assert captured["body"]["model_name"] == (
172
+ "watsonx/ibm/granite-3-8b-instruct#region=eu-de&project=11111111-2222-3333-4444-555555555555"
173
+ )
174
+
175
+
176
+ def test_llm_providers_update_flags_override_an_existing_settings_suffix(
177
+ install_build_client: Callable[[Handler], None],
178
+ ) -> None:
179
+ captured: dict[str, dict[str, str]] = {}
180
+
181
+ def handler(request: httpx2.Request) -> httpx2.Response:
182
+ captured["body"] = json.loads(request.content)
183
+ return httpx2.Response(200, json={"message": "updated"})
184
+
185
+ install_build_client(handler)
186
+ result = runner.invoke(
187
+ app,
188
+ [
189
+ "llm-providers",
190
+ "update",
191
+ "llm1",
192
+ "--name",
193
+ "watsonx",
194
+ "--provider",
195
+ "watsonx",
196
+ "--model-name",
197
+ "watsonx/ibm/granite-3-8b-instruct#region=us-south&project=p1",
198
+ "--region",
199
+ "eu-de",
200
+ ],
201
+ )
202
+ assert result.exit_code == 0, result.output
203
+ assert captured["body"]["model_name"] == "watsonx/ibm/granite-3-8b-instruct#region=eu-de&project=p1"
204
+
205
+
141
206
  def test_llm_providers_update_keeps_null_api_key(install_build_client: Callable[[Handler], None]) -> None:
142
207
  captured: dict[str, object] = {}
143
208
 
File without changes
File without changes
File without changes