imbue-mngr-pi-coding-usage 0.1.0__py3-none-any.whl

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.
File without changes
@@ -0,0 +1,67 @@
1
+ """pi usage data provider for `mngr usage` (reader + writer gate).
2
+
3
+ pi loads exactly one explicit extension -- mngr_pi_coding's lifecycle extension,
4
+ launched with ``pi -e`` -- so the usage *writer* lives there (it already holds
5
+ each assistant message's cost + tokens). This package owns the two pieces that
6
+ are genuinely usage-specific:
7
+
8
+ 1. A gate marker, provisioned per pi agent, that tells the lifecycle extension to
9
+ emit usage events. Without it the writer stays inert -- so usage events are
10
+ only written when *this* package (which ships their reader) is installed.
11
+ Emitting them unread would let ``mngr usage`` fall back to the wrong
12
+ (process-cumulative) strategy and undercount pi's per-message events.
13
+ 2. The reader: an ``aggregate_usage_source`` hookimpl claiming the ``pi-coding``
14
+ source, aggregated session-incrementally (pi reports cost per message).
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from imbue.mngr import hookimpl
20
+ from imbue.mngr.config.data_types import MngrContext
21
+ from imbue.mngr.hosts.host import get_agent_state_dir_path
22
+ from imbue.mngr.interfaces.agent import AgentInterface
23
+ from imbue.mngr.interfaces.host import OnlineHostInterface
24
+ from imbue.mngr_pi_coding.plugin import PiCodingAgent
25
+ from imbue.mngr_usage.api import aggregate_session_incremental
26
+ from imbue.mngr_usage.data_types import UsageEvent
27
+ from imbue.mngr_usage.data_types import UsageSnapshot
28
+
29
+ # Gate marker the lifecycle extension checks (existsSync) to decide whether to
30
+ # emit usage events. Kept in sync with mngr_pi_lifecycle.ts (literal "pi_emit_usage").
31
+ _USAGE_GATE_FILENAME = "pi_emit_usage"
32
+
33
+ # Source the pi writer emits under ($STATE_DIR/events/pi-coding/usage/...); the
34
+ # reader strips "/usage". A fixed harness id (not the agent subtype), so usage
35
+ # from any pi subtype lumps under one source. Kept in sync with mngr_pi_lifecycle.ts.
36
+ _PI_USAGE_SOURCE_NAME = "pi-coding"
37
+
38
+
39
+ @hookimpl
40
+ def on_after_provisioning(agent: AgentInterface, host: OnlineHostInterface, mngr_ctx: MngrContext) -> None:
41
+ """Install the usage gate marker for pi agents so the lifecycle extension emits usage events.
42
+
43
+ Skips non-pi agents. Written to the agent state dir, which the extension
44
+ reads (``$MNGR_AGENT_STATE_DIR/pi_emit_usage``) once at startup.
45
+ """
46
+ if not isinstance(agent, PiCodingAgent):
47
+ return
48
+ state_dir = get_agent_state_dir_path(host.host_dir, agent.id)
49
+ host.write_file(state_dir / _USAGE_GATE_FILENAME, b"1")
50
+
51
+
52
+ @hookimpl
53
+ def aggregate_usage_source(
54
+ source_name: str,
55
+ agents_events: dict[str, list[UsageEvent]],
56
+ since_seconds: int,
57
+ now: int,
58
+ ) -> UsageSnapshot | None:
59
+ """Aggregate the pi usage source; decline every other source.
60
+
61
+ pi reports each assistant message's own cost/tokens (not a cumulative total),
62
+ so this is the session-incremental strategy. Returning None for non-pi sources
63
+ lets the firstresult hook fall through.
64
+ """
65
+ if source_name != _PI_USAGE_SOURCE_NAME:
66
+ return None
67
+ return aggregate_session_incremental(source_name, agents_events, since_seconds=since_seconds, now=now)
@@ -0,0 +1,41 @@
1
+ Metadata-Version: 2.4
2
+ Name: imbue-mngr-pi-coding-usage
3
+ Version: 0.1.0
4
+ Summary: pi usage data provider for `mngr usage` (reader + writer gate; the per-message writer lives in mngr_pi_coding's lifecycle extension)
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: imbue-mngr-pi-coding==0.1.13
7
+ Requires-Dist: imbue-mngr-usage==0.1.5
8
+ Requires-Dist: imbue-mngr==0.2.16
9
+ Description-Content-Type: text/markdown
10
+
11
+ # imbue-mngr-pi-coding-usage
12
+
13
+ pi data provider for `mngr usage`. pi loads a single explicit extension
14
+ (mngr_pi_coding's lifecycle extension, via `pi -e`), so the per-message usage
15
+ **writer** lives there -- it already holds each assistant message's cost and
16
+ tokens. This package owns the two usage-specific pieces:
17
+
18
+ - **The reader**: an `aggregate_usage_source` hookimpl claiming the `pi-coding`
19
+ source, aggregated session-incrementally (pi reports cost per message, summed
20
+ per session).
21
+ - **The writer gate**: `on_after_provisioning` drops a `pi_emit_usage` marker in
22
+ each pi agent's state dir. The lifecycle extension only emits usage events when
23
+ that marker is present -- so the events are written exactly when their reader
24
+ (this package) is installed.
25
+
26
+ ## What gets captured
27
+
28
+ pi computes per-message cost client-side (`usage.cost.total`), so cost is
29
+ **REPORTED** (no estimation). Each assistant `message_end` appends one
30
+ `cost_snapshot` to `events/pi-coding/usage/events.jsonl` with the reported cost,
31
+ tokens (`input`/`output`/`cacheRead`/`cacheWrite`), the provider-qualified model
32
+ (`<provider>/<model>`), and the session id (the pi session file's basename).
33
+ `cost_mode` is `API_KEY` (pi bills a real provider key).
34
+
35
+ ## Why the writer is in the harness extension
36
+
37
+ Unlike Claude (statusline shim) or OpenCode (auto-loaded `plugin/*.ts`), pi loads
38
+ exactly one explicit extension owned by mngr_pi_coding. A standalone writer would
39
+ require a harness change either way, so the writer is folded into the lifecycle
40
+ extension (which already extracts the message usage), and this package stays the
41
+ reader + gate.
@@ -0,0 +1,6 @@
1
+ imbue/mngr_pi_coding_usage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ imbue/mngr_pi_coding_usage/plugin.py,sha256=OIGa6d98BTMpe144EGMGIBD9Z0ymUqbLP57KHuAJS0k,3076
3
+ imbue_mngr_pi_coding_usage-0.1.0.dist-info/METADATA,sha256=p7eI6wKiGWO6FJzceQdrEJPoXkbdLE4FUZc3ZEZSuWY,2012
4
+ imbue_mngr_pi_coding_usage-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
5
+ imbue_mngr_pi_coding_usage-0.1.0.dist-info/entry_points.txt,sha256=ED8hJD4M8d07CEJIV-TsJzwGtS_00qf5RGQ2ZFT0Nyc,59
6
+ imbue_mngr_pi_coding_usage-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [mngr]
2
+ pi_coding_usage = imbue.mngr_pi_coding_usage.plugin