mobileops-managed-agent 0.3.2__tar.gz → 0.4.1__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 (28) hide show
  1. {mobileops_managed_agent-0.3.2/src/mobileops_managed_agent.egg-info → mobileops_managed_agent-0.4.1}/PKG-INFO +7 -1
  2. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/README.md +6 -0
  3. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/pyproject.toml +1 -1
  4. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/__init__.py +2 -0
  5. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/auth.py +2 -1
  6. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/client.py +3 -0
  7. mobileops_managed_agent-0.4.1/src/mobileops_managed_agent/run_timing.py +65 -0
  8. mobileops_managed_agent-0.4.1/src/mobileops_managed_agent/stream_policy.py +33 -0
  9. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1/src/mobileops_managed_agent.egg-info}/PKG-INFO +7 -1
  10. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent.egg-info/SOURCES.txt +2 -0
  11. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/MANIFEST.in +0 -0
  12. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/setup.cfg +0 -0
  13. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/artifacts.py +0 -0
  14. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/assistant_model.py +0 -0
  15. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/assistants.py +0 -0
  16. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/capabilities.py +0 -0
  17. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/credential_vault.py +0 -0
  18. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/files.py +0 -0
  19. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/model_retry.py +0 -0
  20. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/resources.py +0 -0
  21. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/response_schema.py +0 -0
  22. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/sandbox_profile.py +0 -0
  23. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/sandbox_profiles.py +0 -0
  24. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/tool_permissions.py +0 -0
  25. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent/vaults.py +0 -0
  26. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent.egg-info/dependency_links.txt +0 -0
  27. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent.egg-info/requires.txt +0 -0
  28. {mobileops_managed_agent-0.3.2 → mobileops_managed_agent-0.4.1}/src/mobileops_managed_agent.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mobileops-managed-agent
3
- Version: 0.3.2
3
+ Version: 0.4.1
4
4
  Summary: Server-side SDK for the MobileOPS Managed Agent API.
5
5
  Project-URL: Homepage, https://github.com/InnoMindX/deepagent/tree/main/managed-agent-sdk/python
6
6
  Project-URL: Repository, https://github.com/InnoMindX/deepagent
@@ -76,3 +76,9 @@ await client.files.list(thread["thread_id"], "/workspace")
76
76
  ```
77
77
 
78
78
  Keep MobileOPS Managed Agent API keys in server-side code only.
79
+
80
+ ## Streaming in 0.4.0
81
+
82
+ Run streaming defaults to `messages-tuple`, `updates`, and `custom`. Explicit
83
+ `values` subscriptions raise `ValueError` before network access. Fetch full state
84
+ once with `client.agent.threads.get_state(thread_id)` after the stream ends.
@@ -60,3 +60,9 @@ await client.files.list(thread["thread_id"], "/workspace")
60
60
  ```
61
61
 
62
62
  Keep MobileOPS Managed Agent API keys in server-side code only.
63
+
64
+ ## Streaming in 0.4.0
65
+
66
+ Run streaming defaults to `messages-tuple`, `updates`, and `custom`. Explicit
67
+ `values` subscriptions raise `ValueError` before network access. Fetch full state
68
+ once with `client.agent.threads.get_state(thread_id)` after the stream ends.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mobileops-managed-agent"
7
- version = "0.3.2"
7
+ version = "0.4.1"
8
8
  description = "Server-side SDK for the MobileOPS Managed Agent API."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -67,6 +67,7 @@ from .vaults import (
67
67
  __all__ = [
68
68
  "ModelRetryEvent",
69
69
  "ModelRetryTracker",
70
+ "human_run_duration_ms",
70
71
  "MobileOpsArtifactsClient",
71
72
  "MobileOpsStorageClient",
72
73
  "Artifact",
@@ -135,3 +136,4 @@ from .artifacts import (
135
136
  )
136
137
 
137
138
  from .model_retry import ModelRetryEvent, ModelRetryTracker
139
+ from .run_timing import human_run_duration_ms
@@ -10,9 +10,10 @@ def build_headers(
10
10
  safe_headers = {
11
11
  name: value
12
12
  for name, value in dict(headers or {}).items()
13
- if name.lower() != "authorization"
13
+ if name.lower() not in {"authorization", "x-mobileops-sdk"}
14
14
  }
15
15
  return {
16
16
  **safe_headers,
17
+ "X-MobileOps-SDK": "python",
17
18
  "Authorization": f"Bearer {api_key}",
18
19
  }
@@ -6,6 +6,7 @@ from typing import Any
6
6
  import httpx
7
7
 
8
8
  from .auth import build_headers
9
+ from .stream_policy import RunsClient
9
10
  from .files import MobileOpsFilesClient
10
11
  from .assistants import MobileOpsAssistantsClient
11
12
  from .resources import MobileOpsResourcesClient
@@ -21,6 +22,8 @@ class MobileOpsAgentClient:
21
22
 
22
23
  def __init__(self, native: Any) -> None:
23
24
  self._native = native
25
+ if getattr(native, "runs", None) is not None:
26
+ self.runs = RunsClient(native.runs)
24
27
  native_assistants = getattr(native, "assistants", None)
25
28
  if native_assistants is not None:
26
29
  self.assistants = MobileOpsAssistantsClient(native_assistants)
@@ -0,0 +1,65 @@
1
+ """Read turn duration from timestamps stamped on the current human message."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime, timezone
6
+ from typing import Any
7
+
8
+ RUN_STARTED_AT = "run_started_at"
9
+ RUN_ENDED_AT = "run_ended_at"
10
+
11
+
12
+ def _kwargs(message: Any) -> dict[str, Any]:
13
+ if isinstance(message, dict):
14
+ raw = message.get("additional_kwargs")
15
+ else:
16
+ raw = getattr(message, "additional_kwargs", None)
17
+ return raw if isinstance(raw, dict) else {}
18
+
19
+
20
+ def _kind(message: Any) -> str:
21
+ if isinstance(message, dict):
22
+ value = message.get("type") or message.get("role")
23
+ else:
24
+ value = getattr(message, "type", None) or getattr(message, "role", None)
25
+ return str(value or "")
26
+
27
+
28
+ def _parse_ms(value: str) -> int | None:
29
+ text = value[:-1] + "+00:00" if value.endswith("Z") else value
30
+ try:
31
+ parsed = datetime.fromisoformat(text)
32
+ except ValueError:
33
+ return None
34
+ if parsed.tzinfo is None:
35
+ parsed = parsed.replace(tzinfo=timezone.utc)
36
+ return int(parsed.timestamp() * 1000)
37
+
38
+
39
+ def human_run_duration_ms(message: Any, now_ms: int | None = None) -> int | None:
40
+ """Milliseconds for this human turn. Uses now while still running."""
41
+ if message is None:
42
+ return None
43
+ kind = _kind(message)
44
+ if kind and kind not in {"human", "user"}:
45
+ return None
46
+ kwargs = _kwargs(message)
47
+ started = kwargs.get(RUN_STARTED_AT)
48
+ if not isinstance(started, str) or not started:
49
+ return None
50
+ started_ms = _parse_ms(started)
51
+ if started_ms is None:
52
+ return None
53
+ ended = kwargs.get(RUN_ENDED_AT)
54
+ if isinstance(ended, str) and ended:
55
+ ended_ms = _parse_ms(ended)
56
+ else:
57
+ ended_ms = (
58
+ now_ms
59
+ if now_ms is not None
60
+ else int(datetime.now(timezone.utc).timestamp() * 1000)
61
+ )
62
+ if ended_ms is None:
63
+ return None
64
+ duration = ended_ms - started_ms
65
+ return duration if duration >= 0 else None
@@ -0,0 +1,33 @@
1
+ """MobileOPS event modes; full state remains available through get_state."""
2
+ DEFAULT_MODES = ("messages-tuple", "updates", "custom")
3
+
4
+
5
+ def normalize_stream_mode(mode):
6
+ if mode is None or mode == [] or mode == ():
7
+ return list(DEFAULT_MODES)
8
+ if "values" in ([mode] if isinstance(mode, str) else mode):
9
+ raise ValueError("MobileOPS does not support the values stream mode. Use messages, updates and custom; call get_state() after streaming.")
10
+ return mode
11
+
12
+
13
+ class RunsClient:
14
+ def __init__(self, native):
15
+ self._native = native
16
+
17
+ def __getattr__(self, name):
18
+ method = getattr(self._native, name)
19
+ if name == "create_batch":
20
+ def batch(payloads, **kwargs):
21
+ prepared = []
22
+ for payload in payloads:
23
+ mode = normalize_stream_mode(payload.get("stream_mode"))
24
+ prepared.append({**payload, "stream_mode": mode})
25
+ return method(prepared, **kwargs)
26
+ return batch
27
+ if name not in {"stream", "create", "join_stream"}:
28
+ return method
29
+
30
+ def call(*args, **kwargs):
31
+ modes = normalize_stream_mode(kwargs.get("stream_mode"))
32
+ return method(*args, **{**kwargs, "stream_mode": modes})
33
+ return call
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mobileops-managed-agent
3
- Version: 0.3.2
3
+ Version: 0.4.1
4
4
  Summary: Server-side SDK for the MobileOPS Managed Agent API.
5
5
  Project-URL: Homepage, https://github.com/InnoMindX/deepagent/tree/main/managed-agent-sdk/python
6
6
  Project-URL: Repository, https://github.com/InnoMindX/deepagent
@@ -76,3 +76,9 @@ await client.files.list(thread["thread_id"], "/workspace")
76
76
  ```
77
77
 
78
78
  Keep MobileOPS Managed Agent API keys in server-side code only.
79
+
80
+ ## Streaming in 0.4.0
81
+
82
+ Run streaming defaults to `messages-tuple`, `updates`, and `custom`. Explicit
83
+ `values` subscriptions raise `ValueError` before network access. Fetch full state
84
+ once with `client.agent.threads.get_state(thread_id)` after the stream ends.
@@ -13,8 +13,10 @@ src/mobileops_managed_agent/files.py
13
13
  src/mobileops_managed_agent/model_retry.py
14
14
  src/mobileops_managed_agent/resources.py
15
15
  src/mobileops_managed_agent/response_schema.py
16
+ src/mobileops_managed_agent/run_timing.py
16
17
  src/mobileops_managed_agent/sandbox_profile.py
17
18
  src/mobileops_managed_agent/sandbox_profiles.py
19
+ src/mobileops_managed_agent/stream_policy.py
18
20
  src/mobileops_managed_agent/tool_permissions.py
19
21
  src/mobileops_managed_agent/vaults.py
20
22
  src/mobileops_managed_agent.egg-info/PKG-INFO