venice-cli 0.83.2__tar.gz → 0.83.3__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.
- {venice_cli-0.83.2 → venice_cli-0.83.3}/PKG-INFO +7 -1
- {venice_cli-0.83.2 → venice_cli-0.83.3}/README.md +6 -0
- venice_cli-0.83.3/src/venice/__init__.py +1 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_agent.py +6 -1
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_compact.py +6 -1
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_openai.py +48 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_review.py +5 -2
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/code.py +10 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_code_command.py +53 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_compact.py +17 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_review.py +26 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_shared_openai.py +35 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_spawn.py +22 -0
- venice_cli-0.83.2/src/venice/__init__.py +0 -1
- {venice_cli-0.83.2 → venice_cli-0.83.3}/.gitignore +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/CONTRIBUTING.md +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/LICENSE +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/Makefile +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/SECURITY.md +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/bin/venice +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/install.sh +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/pyproject.toml +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/__main__.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/audio_player.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/audio_post.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/auth.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/billing.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/cli.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/client.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/__init__.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_audio.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_browser.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_code.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_exec.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_index.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_mailbox.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_mcp.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_mcp_client.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_memory.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_models.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_persona.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_queue.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_repl.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_session.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_shared.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/_steer.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/balance.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/bg_remove.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/chat.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/completion.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/config.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/contact_sheet.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/embed.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/image.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/image_edit.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/index.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/login.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/master.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/mcp_serve.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/memory.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/models.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/music.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/review.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/search.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/secret.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/sessions.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/sfx.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/tts.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/upscale.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/commands/video.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/config.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/image_montage.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/mcp_server.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/src/venice/userconfig.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/__init__.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/_drive.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/_hygiene.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/_mcp_fake_server.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/_venice_fake_server.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_agent.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_auth.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_balance.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_bg_remove.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_browser.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_chat.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_cli.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_client.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_code.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_completion.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_config.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_contact_sheet.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_drive_cli.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_embed.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_exec.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_fake_server.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_image.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_image_edit.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_index.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_login.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_mailbox.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_master.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_mcp_client.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_mcp_serve.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_mcp_tools.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_memory.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_models.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_music.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_persona.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_planner.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_profiles.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_repl.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_search.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_secret.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_sessions.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_sfx.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_shared.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_source_hygiene.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_steer.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_tts.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_upscale.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_video.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/tests/test_web_search.py +0 -0
- {venice_cli-0.83.2 → venice_cli-0.83.3}/uninstall.sh +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: venice-cli
|
|
3
|
-
Version: 0.83.
|
|
3
|
+
Version: 0.83.3
|
|
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
|
|
@@ -662,6 +662,12 @@ parameters, `max-tool-calls`, the `venice code` sandbox root, and the running
|
|
|
662
662
|
token/cost usage — so resuming restores the whole context, not just the messages.
|
|
663
663
|
The API key is never written to a session.
|
|
664
664
|
|
|
665
|
+
`venice code` also assigns each session an opaque `prompt_cache_key`, sent as an
|
|
666
|
+
OpenAI-compatible routing hint so successive plan, tool-loop, and verification calls
|
|
667
|
+
stay on cache-affine backend infrastructure when the provider supports it. The key is
|
|
668
|
+
not a credential. It survives `--resume` and `/reset`; disposable subagents receive
|
|
669
|
+
their own keys, while compaction's deliberately fresh summary request receives none.
|
|
670
|
+
|
|
665
671
|
One-shot `venice code "task"` runs are sessions too (they persist unless
|
|
666
672
|
`--ephemeral`), so an unattended `--auto` run is resumable, inspectable, and —
|
|
667
673
|
new in this release — **steerable while it runs** (see below).
|
|
@@ -622,6 +622,12 @@ parameters, `max-tool-calls`, the `venice code` sandbox root, and the running
|
|
|
622
622
|
token/cost usage — so resuming restores the whole context, not just the messages.
|
|
623
623
|
The API key is never written to a session.
|
|
624
624
|
|
|
625
|
+
`venice code` also assigns each session an opaque `prompt_cache_key`, sent as an
|
|
626
|
+
OpenAI-compatible routing hint so successive plan, tool-loop, and verification calls
|
|
627
|
+
stay on cache-affine backend infrastructure when the provider supports it. The key is
|
|
628
|
+
not a credential. It survives `--resume` and `/reset`; disposable subagents receive
|
|
629
|
+
their own keys, while compaction's deliberately fresh summary request receives none.
|
|
630
|
+
|
|
625
631
|
One-shot `venice code "task"` runs are sessions too (they persist unless
|
|
626
632
|
`--ephemeral`), so an unattended `--auto` run is resumable, inspectable, and —
|
|
627
633
|
new in this release — **steerable while it runs** (see below).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.83.3"
|
|
@@ -46,6 +46,7 @@ from . import _mcp
|
|
|
46
46
|
from . import _memory
|
|
47
47
|
from . import _models
|
|
48
48
|
from . import _compact
|
|
49
|
+
from . import _openai
|
|
49
50
|
from .models import MODEL_TYPES
|
|
50
51
|
|
|
51
52
|
|
|
@@ -3400,9 +3401,13 @@ def _run_disposable(
|
|
|
3400
3401
|
{"role": "system", "content": sys_prompt},
|
|
3401
3402
|
{"role": "user", "content": task},
|
|
3402
3403
|
]
|
|
3404
|
+
# #128: this is a fresh conversation, not a continuation of the parent. Replace
|
|
3405
|
+
# (rather than inherit) its routing key; every call inside this disposable loop
|
|
3406
|
+
# then shares affinity, while two workers cannot collide on one backend identity.
|
|
3407
|
+
child_kwargs = _openai.with_prompt_cache_key(base_kwargs)
|
|
3403
3408
|
with _capture_stdout() as buf:
|
|
3404
3409
|
run_loop(
|
|
3405
|
-
oai, model, messages,
|
|
3410
|
+
oai, model, messages, child_kwargs, tools,
|
|
3406
3411
|
max_tool_calls=max_tool_calls, yes=True, json_out=False,
|
|
3407
3412
|
budget=budget, ledger=ledger,
|
|
3408
3413
|
)
|
|
@@ -50,6 +50,8 @@ import time
|
|
|
50
50
|
from dataclasses import dataclass
|
|
51
51
|
from typing import List, Optional, Tuple
|
|
52
52
|
|
|
53
|
+
from . import _openai
|
|
54
|
+
|
|
53
55
|
# Rough chars-per-token for English/code text. Deliberately conservative
|
|
54
56
|
# (overestimate tokens) so the fallback triggers compaction a little early
|
|
55
57
|
# rather than a little late; real counts from `usage` override it anyway.
|
|
@@ -315,7 +317,10 @@ def compact_messages(
|
|
|
315
317
|
prefix, tail = split
|
|
316
318
|
sys_msgs = messages[: len(messages) - len(prefix) - len(tail)]
|
|
317
319
|
|
|
318
|
-
|
|
320
|
+
# #128: compaction is a deliberately fresh summarization conversation. Reusing
|
|
321
|
+
# the parent session's routing identity would mix unrelated prefixes on one cache
|
|
322
|
+
# affinity key; preserve all other generation/Venice parameters while stripping it.
|
|
323
|
+
kwargs = _openai.without_prompt_cache_key(base_kwargs)
|
|
319
324
|
kwargs.pop("stream", None)
|
|
320
325
|
kwargs.pop("stream_options", None)
|
|
321
326
|
kwargs.pop("tools", None)
|
|
@@ -13,7 +13,55 @@ catalog, so the missing-SDK path never touches the network.
|
|
|
13
13
|
"""
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
import os
|
|
16
17
|
import sys
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
_PROMPT_CACHE_KEY = "prompt_cache_key"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def prompt_cache_key(kwargs: Optional[dict]) -> Optional[str]:
|
|
25
|
+
"""The OpenAI-compatible cache-affinity key carried by ``extra_body``.
|
|
26
|
+
|
|
27
|
+
The project supports ``openai>=1.40``, predating the SDK's typed
|
|
28
|
+
``prompt_cache_key`` argument. Keeping the wire field in ``extra_body`` makes it
|
|
29
|
+
available on every supported SDK version; the SDK merges it into the request's
|
|
30
|
+
top-level JSON object.
|
|
31
|
+
"""
|
|
32
|
+
extra = (kwargs or {}).get("extra_body")
|
|
33
|
+
value = extra.get(_PROMPT_CACHE_KEY) if isinstance(extra, dict) else None
|
|
34
|
+
return value if isinstance(value, str) and value else None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def with_prompt_cache_key(kwargs: Optional[dict], key: Optional[str] = None) -> dict:
|
|
38
|
+
"""Copy generation kwargs and set one opaque prompt-cache routing key.
|
|
39
|
+
|
|
40
|
+
A missing ``key`` mints a new conversation identity. Nested ``extra_body`` is
|
|
41
|
+
copied before mutation so a disposable subagent can replace its parent's key
|
|
42
|
+
without changing the parent session or its Venice extension parameters.
|
|
43
|
+
"""
|
|
44
|
+
out = dict(kwargs or {})
|
|
45
|
+
extra = out.get("extra_body")
|
|
46
|
+
extra = dict(extra) if isinstance(extra, dict) else {}
|
|
47
|
+
extra[_PROMPT_CACHE_KEY] = key or f"venice-{os.urandom(16).hex()}"
|
|
48
|
+
out["extra_body"] = extra
|
|
49
|
+
return out
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def without_prompt_cache_key(kwargs: Optional[dict]) -> dict:
|
|
53
|
+
"""Copy generation kwargs without a parent conversation's affinity key."""
|
|
54
|
+
out = dict(kwargs or {})
|
|
55
|
+
extra = out.get("extra_body")
|
|
56
|
+
if not isinstance(extra, dict) or _PROMPT_CACHE_KEY not in extra:
|
|
57
|
+
return out
|
|
58
|
+
extra = dict(extra)
|
|
59
|
+
extra.pop(_PROMPT_CACHE_KEY, None)
|
|
60
|
+
if extra:
|
|
61
|
+
out["extra_body"] = extra
|
|
62
|
+
else:
|
|
63
|
+
out.pop("extra_body", None)
|
|
64
|
+
return out
|
|
17
65
|
|
|
18
66
|
|
|
19
67
|
def import_openai(label: str):
|
|
@@ -56,7 +56,7 @@ import threading
|
|
|
56
56
|
import time
|
|
57
57
|
from typing import Dict, List, Optional, Tuple
|
|
58
58
|
|
|
59
|
-
from . import _agent, _code, _exec
|
|
59
|
+
from . import _agent, _code, _exec, _openai
|
|
60
60
|
from ._exec import ( # shared exec rails (#33): one gate for every git shell-out
|
|
61
61
|
DEFAULT_EXEC_TIMEOUT,
|
|
62
62
|
MAX_OUTPUT_CHARS,
|
|
@@ -584,6 +584,9 @@ def _retry_for_verdict(oai, model: str, report: str, base_kwargs: dict,
|
|
|
584
584
|
it is supposed to be bounded by.
|
|
585
585
|
"""
|
|
586
586
|
_t0 = time.monotonic()
|
|
587
|
+
# #128: this retry is intentionally a fresh one-shot, not a continuation of the
|
|
588
|
+
# author or reviewer conversation, so it must not borrow either affinity identity.
|
|
589
|
+
retry_kwargs = _openai.without_prompt_cache_key(base_kwargs)
|
|
587
590
|
resp = oai.chat.completions.create(
|
|
588
591
|
model=model,
|
|
589
592
|
messages=[
|
|
@@ -591,7 +594,7 @@ def _retry_for_verdict(oai, model: str, report: str, base_kwargs: dict,
|
|
|
591
594
|
{"role": "assistant", "content": report or ""},
|
|
592
595
|
{"role": "user", "content": RETRY_MSG},
|
|
593
596
|
],
|
|
594
|
-
**
|
|
597
|
+
**retry_kwargs,
|
|
595
598
|
)
|
|
596
599
|
if ledger is not None:
|
|
597
600
|
ledger.record(getattr(resp, "usage", None), seconds=time.monotonic() - _t0)
|
|
@@ -715,6 +715,16 @@ def _run(args) -> int:
|
|
|
715
715
|
# venice_scout). `_gen_kwargs` reads only args.temperature/max_tokens -- no
|
|
716
716
|
# dependency on `tools`, so the reorder is safe.
|
|
717
717
|
gen_kwargs = PROFILE.build_gen_kwargs(args)
|
|
718
|
+
# #128: Venice can route requests carrying one stable prompt_cache_key to the
|
|
719
|
+
# same cache-bearing backend. Restore the saved key when one exists; old sessions
|
|
720
|
+
# receive a key on first resume, and fresh/ephemeral runs mint one before the plan
|
|
721
|
+
# turn so plan -> execute -> verify all share affinity. It rides in extra_body for
|
|
722
|
+
# compatibility with the project's openai>=1.40 floor and is persisted automatically
|
|
723
|
+
# with the session's generation kwargs.
|
|
724
|
+
saved_cache_key = (
|
|
725
|
+
_openai.prompt_cache_key(session.gen_kwargs) if session is not None else None
|
|
726
|
+
)
|
|
727
|
+
gen_kwargs = _openai.with_prompt_cache_key(gen_kwargs, saved_cache_key)
|
|
718
728
|
# #52 planner slice: the session's shared dispatch record list. scout/spawn append
|
|
719
729
|
# every launched dispatch to it; venice_merge (and the --json envelope) roll it up.
|
|
720
730
|
dispatches = [] if planner else None
|
|
@@ -138,6 +138,10 @@ class TestCodeCommand(unittest.TestCase):
|
|
|
138
138
|
self.assertEqual(calls[0]["tool_choice"], "none") # plan
|
|
139
139
|
self.assertEqual(calls[1]["tool_choice"], "auto") # execute loop
|
|
140
140
|
self.assertEqual(calls[3]["tool_choice"], "none") # acceptance check
|
|
141
|
+
# #128: every phase of one top-level conversation shares backend affinity.
|
|
142
|
+
keys = [c["extra_body"]["prompt_cache_key"] for c in calls]
|
|
143
|
+
self.assertTrue(keys[0].startswith("venice-"))
|
|
144
|
+
self.assertEqual(len(set(keys)), 1)
|
|
141
145
|
# auto -> confirm=True -> the write actually happened
|
|
142
146
|
with open(os.path.join(self.root, "hello.py")) as f:
|
|
143
147
|
self.assertEqual(f.read(), "def hi():\n return 1\n")
|
|
@@ -505,6 +509,7 @@ class TestCodeCommand(unittest.TestCase):
|
|
|
505
509
|
captured["root"] = root
|
|
506
510
|
captured["system_reseed"] = system_reseed
|
|
507
511
|
captured["ledger"] = kw.get("ledger")
|
|
512
|
+
captured["gen_kwargs"] = gen_kwargs
|
|
508
513
|
return 0
|
|
509
514
|
|
|
510
515
|
stdin = mock.MagicMock()
|
|
@@ -530,6 +535,9 @@ class TestCodeCommand(unittest.TestCase):
|
|
|
530
535
|
# #117: the REPL is HANDED the hoisted ledger rather than building its own,
|
|
531
536
|
# because the rails already mirror into that object.
|
|
532
537
|
self.assertIsNotNone(captured["ledger"])
|
|
538
|
+
self.assertTrue(
|
|
539
|
+
captured["gen_kwargs"]["extra_body"]["prompt_cache_key"].startswith(
|
|
540
|
+
"venice-"))
|
|
533
541
|
|
|
534
542
|
# --- session resume (#47) ---
|
|
535
543
|
def _mk_zone(self):
|
|
@@ -612,6 +620,51 @@ class TestCodeCommand(unittest.TestCase):
|
|
|
612
620
|
self.assertEqual(captured["root"], saved_root) # faithful restore
|
|
613
621
|
self.assertEqual(captured["session_id"], sess.id)
|
|
614
622
|
|
|
623
|
+
def test_prompt_cache_key_survives_save_reset_and_resume(self):
|
|
624
|
+
zone, _session = self._mk_zone()
|
|
625
|
+
rc, first_calls = self._run_interactive(
|
|
626
|
+
_code_args(task="first", root=self.root, interactive=True, auto=True),
|
|
627
|
+
[FakeToolCompletion("done")], ["/exit"], zone,
|
|
628
|
+
)
|
|
629
|
+
self.assertEqual(rc, 0)
|
|
630
|
+
first_key = first_calls[0]["extra_body"]["prompt_cache_key"]
|
|
631
|
+
|
|
632
|
+
with mock.patch.dict(os.environ, {"VENICE_SESSIONS_DIR": zone}):
|
|
633
|
+
saved = _session.load(_session.most_recent("code").id, "code")
|
|
634
|
+
self.assertEqual(
|
|
635
|
+
saved.gen_kwargs["extra_body"]["prompt_cache_key"], first_key)
|
|
636
|
+
|
|
637
|
+
rc, resumed_calls = self._run_interactive(
|
|
638
|
+
_code_args(resume=saved.id, root=self.root, auto=True),
|
|
639
|
+
[FakeToolCompletion("done again")],
|
|
640
|
+
["/reset", "second", "/exit"], zone,
|
|
641
|
+
)
|
|
642
|
+
self.assertEqual(rc, 0)
|
|
643
|
+
self.assertEqual(
|
|
644
|
+
resumed_calls[0]["extra_body"]["prompt_cache_key"], first_key)
|
|
645
|
+
|
|
646
|
+
def test_old_session_without_a_cache_key_is_upgraded_on_resume(self):
|
|
647
|
+
zone, _session = self._mk_zone()
|
|
648
|
+
old = _session.new_session(
|
|
649
|
+
"code", label="venice code", model="llama-3.3-70b", root=self.root,
|
|
650
|
+
gen_kwargs={"temperature": 0.4},
|
|
651
|
+
messages=[{"role": "system", "content": "old"}],
|
|
652
|
+
)
|
|
653
|
+
with mock.patch.dict(os.environ, {"VENICE_SESSIONS_DIR": zone}):
|
|
654
|
+
_session.save(old)
|
|
655
|
+
|
|
656
|
+
rc, calls = self._run_interactive(
|
|
657
|
+
_code_args(resume=old.id, root=self.root, auto=True),
|
|
658
|
+
[FakeToolCompletion("continued")], ["continue", "/exit"], zone,
|
|
659
|
+
)
|
|
660
|
+
self.assertEqual(rc, 0)
|
|
661
|
+
key = calls[0]["extra_body"]["prompt_cache_key"]
|
|
662
|
+
self.assertTrue(key.startswith("venice-"))
|
|
663
|
+
with mock.patch.dict(os.environ, {"VENICE_SESSIONS_DIR": zone}):
|
|
664
|
+
upgraded = _session.load(old.id, "code")
|
|
665
|
+
self.assertEqual(
|
|
666
|
+
upgraded.gen_kwargs["extra_body"]["prompt_cache_key"], key)
|
|
667
|
+
|
|
615
668
|
|
|
616
669
|
class TestOneShotSteering(unittest.TestCase):
|
|
617
670
|
"""#78: one-shot `venice code` runs persist a steerable session + drain steers.
|
|
@@ -259,6 +259,23 @@ class TestCompactMessages(unittest.TestCase):
|
|
|
259
259
|
self.assertEqual(calls[0]["temperature"], 0.2)
|
|
260
260
|
self.assertEqual(calls[0]["max_tokens"], _compact.SUMMARY_MAX_TOKENS)
|
|
261
261
|
|
|
262
|
+
def test_fresh_summary_does_not_reuse_parent_cache_affinity(self):
|
|
263
|
+
msgs = _history(6)
|
|
264
|
+
fake, calls = _fake_oai()
|
|
265
|
+
original = {"extra_body": {
|
|
266
|
+
"prompt_cache_key": "parent-key",
|
|
267
|
+
"venice_parameters": {"include_venice_system_prompt": False},
|
|
268
|
+
}}
|
|
269
|
+
_compact.compact_messages(
|
|
270
|
+
fake, "m", msgs, keep_turns=2, base_kwargs=original)
|
|
271
|
+
self.assertNotIn("prompt_cache_key", calls[0]["extra_body"])
|
|
272
|
+
self.assertEqual(
|
|
273
|
+
calls[0]["extra_body"]["venice_parameters"],
|
|
274
|
+
{"include_venice_system_prompt": False},
|
|
275
|
+
)
|
|
276
|
+
self.assertEqual(
|
|
277
|
+
original["extra_body"]["prompt_cache_key"], "parent-key")
|
|
278
|
+
|
|
262
279
|
|
|
263
280
|
class TestBuildSummaryPrompt(unittest.TestCase):
|
|
264
281
|
def test_tool_messages_rendered_as_text(self):
|
|
@@ -696,6 +696,32 @@ class TestRunCycle(_RepoBase):
|
|
|
696
696
|
rounds=1)
|
|
697
697
|
self.assertEqual(out["verdict"], "clean")
|
|
698
698
|
|
|
699
|
+
def test_verdict_retry_does_not_reuse_parent_cache_affinity(self):
|
|
700
|
+
oai = mock.MagicMock()
|
|
701
|
+
oai.chat.completions.create.return_value = FakeToolCompletion(
|
|
702
|
+
content="REVIEW: CLEAN")
|
|
703
|
+
|
|
704
|
+
def _stub(*a, **kw):
|
|
705
|
+
return {"status": "ok", "tool_calls": 0, "truncated": False,
|
|
706
|
+
"report": "SCOPE: x\nFINDINGS: none\n(no sentinel)"}
|
|
707
|
+
|
|
708
|
+
base = {"extra_body": {
|
|
709
|
+
"prompt_cache_key": "parent-key",
|
|
710
|
+
"venice_parameters": {"include_venice_system_prompt": False},
|
|
711
|
+
}}
|
|
712
|
+
with mock.patch.object(_agent, "run_review", _stub):
|
|
713
|
+
out = _review.run_cycle(
|
|
714
|
+
oai, "m", self.collected, base, root=self.root, rounds=1)
|
|
715
|
+
self.assertEqual(out["verdict"], "clean")
|
|
716
|
+
sent = oai.chat.completions.create.call_args.kwargs
|
|
717
|
+
self.assertNotIn("prompt_cache_key", sent["extra_body"])
|
|
718
|
+
self.assertEqual(
|
|
719
|
+
sent["extra_body"]["venice_parameters"],
|
|
720
|
+
{"include_venice_system_prompt": False},
|
|
721
|
+
)
|
|
722
|
+
self.assertEqual(
|
|
723
|
+
base["extra_body"]["prompt_cache_key"], "parent-key")
|
|
724
|
+
|
|
699
725
|
def test_verdict_retry_tokens_are_counted(self):
|
|
700
726
|
# The retry runs outside `_run_disposable`, so nothing else records it. An
|
|
701
727
|
# uncounted call is also an UNCAPPED one -- `--max-tokens` was silently not
|
|
@@ -182,6 +182,41 @@ class TestSupportsCapability(unittest.TestCase):
|
|
|
182
182
|
|
|
183
183
|
# --- _openai ---
|
|
184
184
|
|
|
185
|
+
|
|
186
|
+
class TestPromptCacheAffinity(unittest.TestCase):
|
|
187
|
+
def test_mints_an_opaque_key_without_mutating_the_input(self):
|
|
188
|
+
original = {"temperature": 0.2}
|
|
189
|
+
out = _openai.with_prompt_cache_key(original)
|
|
190
|
+
key = _openai.prompt_cache_key(out)
|
|
191
|
+
self.assertIsInstance(key, str)
|
|
192
|
+
self.assertTrue(key.startswith("venice-"))
|
|
193
|
+
self.assertNotIn("extra_body", original)
|
|
194
|
+
|
|
195
|
+
def test_explicit_key_preserves_venice_parameters(self):
|
|
196
|
+
original = {"extra_body": {"venice_parameters": {"enable_web_search": True}}}
|
|
197
|
+
out = _openai.with_prompt_cache_key(original, "session-key")
|
|
198
|
+
self.assertEqual(_openai.prompt_cache_key(out), "session-key")
|
|
199
|
+
self.assertEqual(out["extra_body"]["venice_parameters"],
|
|
200
|
+
{"enable_web_search": True})
|
|
201
|
+
self.assertNotIn("prompt_cache_key", original["extra_body"])
|
|
202
|
+
|
|
203
|
+
def test_strip_keeps_other_extra_body_fields_and_does_not_mutate(self):
|
|
204
|
+
original = {"extra_body": {
|
|
205
|
+
"prompt_cache_key": "parent",
|
|
206
|
+
"venice_parameters": {"character_slug": "x"},
|
|
207
|
+
}}
|
|
208
|
+
out = _openai.without_prompt_cache_key(original)
|
|
209
|
+
self.assertIsNone(_openai.prompt_cache_key(out))
|
|
210
|
+
self.assertEqual(out["extra_body"],
|
|
211
|
+
{"venice_parameters": {"character_slug": "x"}})
|
|
212
|
+
self.assertEqual(_openai.prompt_cache_key(original), "parent")
|
|
213
|
+
|
|
214
|
+
def test_strip_removes_empty_extra_body(self):
|
|
215
|
+
out = _openai.without_prompt_cache_key(
|
|
216
|
+
{"temperature": 0.1, "extra_body": {"prompt_cache_key": "x"}})
|
|
217
|
+
self.assertEqual(out, {"temperature": 0.1})
|
|
218
|
+
|
|
219
|
+
|
|
185
220
|
class _StubConnErr(Exception):
|
|
186
221
|
pass
|
|
187
222
|
|
|
@@ -234,6 +234,28 @@ class TestRunScout(_ScoutBase):
|
|
|
234
234
|
self.assertIn("SCOUT", first_msgs[0]["content"])
|
|
235
235
|
self.assertEqual(first_msgs[1], {"role": "user", "content": "investigate"})
|
|
236
236
|
|
|
237
|
+
def test_each_disposable_conversation_replaces_the_parent_cache_key(self):
|
|
238
|
+
parent = {"extra_body": {
|
|
239
|
+
"prompt_cache_key": "parent-key",
|
|
240
|
+
"venice_parameters": {"include_venice_system_prompt": False},
|
|
241
|
+
}}
|
|
242
|
+
fake, calls = _fake_openai_seq([
|
|
243
|
+
FakeToolCompletion(_REPORT), FakeToolCompletion(_REPORT),
|
|
244
|
+
])
|
|
245
|
+
tools = _code.read_only_tools(self.root)
|
|
246
|
+
_agent.run_scout(fake, "m", "one", tools, parent, max_tool_calls=4)
|
|
247
|
+
_agent.run_scout(fake, "m", "two", tools, parent, max_tool_calls=4)
|
|
248
|
+
|
|
249
|
+
keys = [c["extra_body"]["prompt_cache_key"] for c in calls]
|
|
250
|
+
self.assertNotIn("parent-key", keys)
|
|
251
|
+
self.assertNotEqual(keys[0], keys[1])
|
|
252
|
+
for call in calls:
|
|
253
|
+
self.assertEqual(
|
|
254
|
+
call["extra_body"]["venice_parameters"],
|
|
255
|
+
{"include_venice_system_prompt": False},
|
|
256
|
+
)
|
|
257
|
+
self.assertEqual(parent["extra_body"]["prompt_cache_key"], "parent-key")
|
|
258
|
+
|
|
237
259
|
def test_focus_hint_threaded_into_system(self):
|
|
238
260
|
seq = [FakeToolCompletion(_REPORT)]
|
|
239
261
|
fake, calls = _fake_openai_seq(seq)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.83.2"
|
|
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
|
|
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
|
|
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
|