venice-cli 0.34.0__tar.gz → 0.35.0__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 (86) hide show
  1. {venice_cli-0.34.0 → venice_cli-0.35.0}/PKG-INFO +6 -2
  2. {venice_cli-0.34.0 → venice_cli-0.35.0}/README.md +5 -1
  3. venice_cli-0.35.0/src/venice/__init__.py +1 -0
  4. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/_agent.py +24 -0
  5. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/_code.py +5 -0
  6. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/_mcp.py +31 -0
  7. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_chat.py +6 -4
  8. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_code.py +9 -0
  9. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_mcp_tools.py +34 -0
  10. venice_cli-0.34.0/src/venice/__init__.py +0 -1
  11. {venice_cli-0.34.0 → venice_cli-0.35.0}/.gitignore +0 -0
  12. {venice_cli-0.34.0 → venice_cli-0.35.0}/CONTRIBUTING.md +0 -0
  13. {venice_cli-0.34.0 → venice_cli-0.35.0}/LICENSE +0 -0
  14. {venice_cli-0.34.0 → venice_cli-0.35.0}/Makefile +0 -0
  15. {venice_cli-0.34.0 → venice_cli-0.35.0}/SECURITY.md +0 -0
  16. {venice_cli-0.34.0 → venice_cli-0.35.0}/bin/venice +0 -0
  17. {venice_cli-0.34.0 → venice_cli-0.35.0}/install.sh +0 -0
  18. {venice_cli-0.34.0 → venice_cli-0.35.0}/pyproject.toml +0 -0
  19. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/__main__.py +0 -0
  20. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/audio_player.py +0 -0
  21. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/audio_post.py +0 -0
  22. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/auth.py +0 -0
  23. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/billing.py +0 -0
  24. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/cli.py +0 -0
  25. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/client.py +0 -0
  26. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/__init__.py +0 -0
  27. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/_audio.py +0 -0
  28. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/_index.py +0 -0
  29. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/_mcp_client.py +0 -0
  30. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/_models.py +0 -0
  31. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/_openai.py +0 -0
  32. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/_queue.py +0 -0
  33. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/_repl.py +0 -0
  34. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/_shared.py +0 -0
  35. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/balance.py +0 -0
  36. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/bg_remove.py +0 -0
  37. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/chat.py +0 -0
  38. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/code.py +0 -0
  39. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/config.py +0 -0
  40. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/contact_sheet.py +0 -0
  41. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/embed.py +0 -0
  42. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/image.py +0 -0
  43. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/image_edit.py +0 -0
  44. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/index.py +0 -0
  45. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/login.py +0 -0
  46. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/master.py +0 -0
  47. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/mcp_serve.py +0 -0
  48. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/models.py +0 -0
  49. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/music.py +0 -0
  50. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/search.py +0 -0
  51. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/sfx.py +0 -0
  52. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/tts.py +0 -0
  53. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/upscale.py +0 -0
  54. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/commands/video.py +0 -0
  55. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/config.py +0 -0
  56. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/image_montage.py +0 -0
  57. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/mcp_server.py +0 -0
  58. {venice_cli-0.34.0 → venice_cli-0.35.0}/src/venice/userconfig.py +0 -0
  59. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/__init__.py +0 -0
  60. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/_mcp_fake_server.py +0 -0
  61. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_agent.py +0 -0
  62. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_auth.py +0 -0
  63. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_balance.py +0 -0
  64. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_bg_remove.py +0 -0
  65. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_client.py +0 -0
  66. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_code_command.py +0 -0
  67. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_config.py +0 -0
  68. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_contact_sheet.py +0 -0
  69. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_embed.py +0 -0
  70. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_image.py +0 -0
  71. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_image_edit.py +0 -0
  72. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_index.py +0 -0
  73. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_master.py +0 -0
  74. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_mcp_client.py +0 -0
  75. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_mcp_serve.py +0 -0
  76. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_models.py +0 -0
  77. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_music.py +0 -0
  78. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_repl.py +0 -0
  79. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_search.py +0 -0
  80. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_sfx.py +0 -0
  81. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_shared.py +0 -0
  82. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_shared_openai.py +0 -0
  83. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_tts.py +0 -0
  84. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_upscale.py +0 -0
  85. {venice_cli-0.34.0 → venice_cli-0.35.0}/tests/test_video.py +0 -0
  86. {venice_cli-0.34.0 → venice_cli-0.35.0}/uninstall.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: venice-cli
3
- Version: 0.34.0
3
+ Version: 0.35.0
4
4
  Summary: A stdlib-only Python CLI for the Venice.ai API: audio, video, images, chat, and embeddings.
5
5
  Project-URL: Homepage, https://github.com/gobha-me/venice-cli
6
6
  Project-URL: Repository, https://github.com/gobha-me/venice-cli
@@ -626,7 +626,11 @@ produces a final answer. These run **in-process on the `[openai]` extra alone**
626
626
  seven of the capabilities `venice mcp-serve` exposes (which adds `venice_video`
627
627
  and `venice_image_edit`) — plus `project_search`,
628
628
  a read-only [semantic search](#semantic-search) over the project's local
629
- `venice index` for locating code by meaning before acting on it.
629
+ `venice index` for locating code by meaning before acting on it, and
630
+ `venice_models`, a read-only lookup that lists model ids for a given catalog
631
+ type (its single `type` arg — text/code/image/video/music/tts/embedding/upscale,
632
+ or `all`) so the model can pick a valid `model` for the other tools instead of
633
+ guessing. (`venice code` gets `venice_models` too.)
630
634
 
631
635
  ```sh
632
636
  # One command, multiple steps: the model generates an image, then critiques it.
@@ -588,7 +588,11 @@ produces a final answer. These run **in-process on the `[openai]` extra alone**
588
588
  seven of the capabilities `venice mcp-serve` exposes (which adds `venice_video`
589
589
  and `venice_image_edit`) — plus `project_search`,
590
590
  a read-only [semantic search](#semantic-search) over the project's local
591
- `venice index` for locating code by meaning before acting on it.
591
+ `venice index` for locating code by meaning before acting on it, and
592
+ `venice_models`, a read-only lookup that lists model ids for a given catalog
593
+ type (its single `type` arg — text/code/image/video/music/tts/embedding/upscale,
594
+ or `all`) so the model can pick a valid `model` for the other tools instead of
595
+ guessing. (`venice code` gets `venice_models` too.)
592
596
 
593
597
  ```sh
594
598
  # One command, multiple steps: the model generates an image, then critiques it.
@@ -0,0 +1 @@
1
+ __version__ = "0.35.0"
@@ -32,6 +32,7 @@ from dataclasses import dataclass
32
32
  from typing import Callable, Dict, List, Optional
33
33
 
34
34
  from . import _mcp
35
+ from .models import MODEL_TYPES
35
36
 
36
37
 
37
38
  # --------------------------------------------------------------------------- #
@@ -182,6 +183,19 @@ _SEARCH_SCHEMA = _obj(
182
183
  required=["query"],
183
184
  )
184
185
 
186
+ _MODELS_SCHEMA = _obj(
187
+ {
188
+ "type": {
189
+ "type": "string",
190
+ "enum": ["all", *MODEL_TYPES],
191
+ "description": "Which catalog type to list model ids for "
192
+ "(text, code, image, video, music, tts, embedding, upscale), "
193
+ "or 'all' for a {type: [ids]} map.",
194
+ },
195
+ },
196
+ required=["type"],
197
+ )
198
+
185
199
  # Schema for a tool folded in ONLY via `only=` (e.g. `venice code --assets`), so it
186
200
  # is not part of chat's default advertised set. Curated subset of
187
201
  # `_mcp.image_edit_tool`; `confirm`/`max_spend`/`output_dir` omitted (loop-injected).
@@ -282,6 +296,16 @@ _BUILTINS = [
282
296
  _CHAT_SCHEMA,
283
297
  False,
284
298
  ),
299
+ (
300
+ "venice_models",
301
+ "models_tool",
302
+ "List available Venice model ids for a catalog type (text/code/image/video/"
303
+ "music/tts/embedding/upscale, or 'all') via the free /models catalog. Use it "
304
+ "to choose a valid `model` for the other venice_* tools instead of guessing. "
305
+ "Read-only; not spend-gated.",
306
+ _MODELS_SCHEMA,
307
+ False,
308
+ ),
285
309
  (
286
310
  "project_search",
287
311
  "search_tool",
@@ -527,6 +527,11 @@ def code_tools(
527
527
  _SEARCH_SCHEMA, search_invoke, paid=False,
528
528
  ))
529
529
 
530
+ # A free model-catalog lookup so the agent can pick a valid `model` for the
531
+ # asset tools instead of guessing (needs a client for the /models GET).
532
+ if client is not None:
533
+ tools.extend(_agent.builtin_tools(client, only={"venice_models"}))
534
+
530
535
  if assets and client is not None:
531
536
  asset_dir = os.environ.get("VENICE_MCP_OUTPUT_DIR") or root
532
537
  tools.extend(_agent.builtin_tools(
@@ -29,6 +29,7 @@ from . import bg_remove as _bg
29
29
  from . import chat as _chat
30
30
  from . import image as _image
31
31
  from . import image_edit as _image_edit
32
+ from . import models as _models_cmd
32
33
  from . import music as _music
33
34
  from . import sfx as _sfx
34
35
  from . import tts as _tts
@@ -750,3 +751,33 @@ def search_tool(client, query, *, k: int = 8) -> dict:
750
751
  except _index.IndexingError as e:
751
752
  return _err(str(e) or f"search: failed (exit {e.exit_code})")
752
753
  return {"status": "ok", "results": results, "count": len(results)}
754
+
755
+
756
+ def models_tool(client, *, type: str) -> dict:
757
+ """List available Venice model ids for a catalog `type` (or "all").
758
+
759
+ Wraps the free `/models?type=...` GET (`_models.catalog`) so an agent can
760
+ discover valid model ids before choosing a `model` for the other tools.
761
+ Read-only; not spend-gated. Returns a flat id list for one type, or a
762
+ {type: [ids]} map for "all".
763
+ """
764
+ valid = _models_cmd.MODEL_TYPES
765
+ if type != "all" and type not in valid:
766
+ return _err(
767
+ f"models: unknown type {type!r}; choose from "
768
+ + ", ".join(("all", *valid))
769
+ )
770
+ types = valid if type == "all" else (type,)
771
+ by_type = {}
772
+ for t in types:
773
+ cat = _models.catalog(client, t)
774
+ if cat is None:
775
+ return _err(f"models: /models catalog unavailable for type {t!r}")
776
+ by_type[t] = [m["id"] for m in cat
777
+ if isinstance(m, dict) and m.get("id")]
778
+ if type == "all":
779
+ return {"status": "ok", "type": "all",
780
+ "count": sum(len(v) for v in by_type.values()),
781
+ "models": by_type}
782
+ return {"status": "ok", "type": type,
783
+ "count": len(by_type[type]), "models": by_type[type]}
@@ -505,8 +505,9 @@ class TestChatAgent(unittest.TestCase):
505
505
  f"{t['function']['name']} leaks control kwarg {banned}",
506
506
  )
507
507
  names = {t["function"]["name"] for t in tools}
508
- self.assertEqual(len(names), 8) # seven media/chat tools + project_search
508
+ self.assertEqual(len(names), 9) # media/chat + project_search + venice_models
509
509
  self.assertIn("project_search", names)
510
+ self.assertIn("venice_models", names)
510
511
 
511
512
  def test_tool_error_surfaced_not_fatal(self):
512
513
  seq = [
@@ -636,7 +637,7 @@ class TestChatMcp(unittest.TestCase):
636
637
  names = self._tool_names(calls[0])
637
638
  self.assertIn("fs__read", names) # remote tool advertised
638
639
  self.assertIn("venice_image", names) # alongside the built-ins
639
- self.assertEqual(len(names), 9) # 8 built-ins + 1 remote
640
+ self.assertEqual(len(names), 10) # 9 built-ins + 1 remote
640
641
  self.assertEqual(attach.specs, [("fs", {"command": "srv", "args": []})])
641
642
  tool_msgs = [m for m in calls[1]["messages"] if m.get("role") == "tool"]
642
643
  self.assertIn("127.0.0.1", tool_msgs[0]["content"])
@@ -650,7 +651,7 @@ class TestChatMcp(unittest.TestCase):
650
651
  )
651
652
  self.assertEqual(rc, 0)
652
653
  attach.assert_not_called()
653
- self.assertEqual(len(self._tool_names(calls[0])), 8) # built-ins only
654
+ self.assertEqual(len(self._tool_names(calls[0])), 9) # built-ins only
654
655
 
655
656
  def test_unknown_mcp_server_exits_2_before_model(self):
656
657
  attach = mock.MagicMock()
@@ -738,7 +739,8 @@ class TestBuiltinToolsRegistry(unittest.TestCase):
738
739
  # chat's default advertisement must not grow when code gains asset tools
739
740
  from venice.commands import _agent
740
741
  names = {t.name for t in _agent.builtin_tools(object())}
741
- self.assertEqual(len(names), 8)
742
+ self.assertEqual(len(names), 9) # +venice_models (a deliberate free chat tool)
743
+ self.assertIn("venice_models", names)
742
744
  self.assertNotIn("venice_image_edit", names)
743
745
 
744
746
  def test_only_can_select_code_asset_extra(self):
@@ -246,6 +246,15 @@ class TestCodeFactory(unittest.TestCase):
246
246
  self.assertNotIn("venice_chat", names) # excluded by design
247
247
  self.assertIn("venice_video", names)
248
248
 
249
+ def test_models_tool_present_with_client(self):
250
+ by = {t.name: t for t in _code.code_tools("/tmp", client=object())}
251
+ self.assertIn("venice_models", by) # free model-catalog lookup for the agent
252
+ self.assertFalse(by["venice_models"].paid) # read-only, not spend-gated
253
+
254
+ def test_models_tool_absent_without_client(self):
255
+ names = {t.name for t in _code.code_tools("/tmp")}
256
+ self.assertNotIn("venice_models", names) # needs a client for the /models GET
257
+
249
258
  def test_asset_tools_are_paid(self):
250
259
  by = {t.name: t for t in _code.code_tools("/tmp", client=object(),
251
260
  assets=True)}
@@ -492,5 +492,39 @@ class TestRetrieveBytesParity(unittest.TestCase):
492
492
  _audio.retrieve_bytes(_client(), "m", "q", poll_interval=0, max_wait=-1)
493
493
 
494
494
 
495
+ class TestModelsTool(_ToolTest):
496
+ def _catalog(self, *ids):
497
+ return json.dumps({"data": [{"id": i} for i in ids]}).encode()
498
+
499
+ def test_lists_ids_for_one_type(self):
500
+ with mock.patch("venice.client.urllib.request.urlopen",
501
+ _seq(FakeResp(200, self._catalog("m-a", "m-b")))), \
502
+ self.stdout_guard():
503
+ out = _mcp.models_tool(_client(), type="image")
504
+ self.assertEqual(out["status"], "ok")
505
+ self.assertEqual(out["type"], "image")
506
+ self.assertEqual(out["models"], ["m-a", "m-b"])
507
+ self.assertEqual(out["count"], 2)
508
+
509
+ def test_all_returns_map_keyed_by_type(self):
510
+ from venice.commands.models import MODEL_TYPES
511
+ resps = _seq(*[FakeResp(200, self._catalog(f"{t}-1")) for t in MODEL_TYPES])
512
+ with mock.patch("venice.client.urllib.request.urlopen", resps), \
513
+ self.stdout_guard():
514
+ out = _mcp.models_tool(_client(), type="all")
515
+ self.assertEqual(out["status"], "ok")
516
+ self.assertEqual(set(out["models"]), set(MODEL_TYPES))
517
+ self.assertEqual(out["count"], len(MODEL_TYPES))
518
+
519
+ def test_unknown_type_errors_without_http(self):
520
+ def boom(*a, **kw):
521
+ raise AssertionError("a bad type must not trigger an HTTP call")
522
+ with mock.patch("venice.client.urllib.request.urlopen", boom), \
523
+ self.stdout_guard():
524
+ out = _mcp.models_tool(_client(), type="bogus")
525
+ self.assertEqual(out["status"], "error")
526
+ self.assertIn("unknown type", out["message"])
527
+
528
+
495
529
  if __name__ == "__main__":
496
530
  unittest.main()
@@ -1 +0,0 @@
1
- __version__ = "0.34.0"
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