mainsequence 9.0.9.dev46__tar.gz → 9.0.10.dev47__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 (104) hide show
  1. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/PKG-INFO +1 -1
  2. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/agent_scaffold/skills/mainsequence-inference/SKILL.md +15 -0
  3. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/agent_runtime_models.py +17 -1
  4. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/inference.py +12 -1
  5. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence.egg-info/PKG-INFO +1 -1
  6. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/pyproject.toml +1 -1
  7. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_inference_client.py +30 -0
  8. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/LICENSE +0 -0
  9. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/README.md +0 -0
  10. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/agent_scaffold/AGENTS.md +0 -0
  11. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
  12. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  13. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  14. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/agent_scaffold/skills/maintenance/code_repository_maintenance/SKILL.md +0 -0
  15. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/agent_scaffold/skills/maintenance/metatables_transition/SKILL.md +0 -0
  16. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  17. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  18. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/agent_scaffold/skills/sdk_code_repository_execution/SKILL.md +0 -0
  19. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/__init__.py +0 -0
  20. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/__main__.py +0 -0
  21. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/_request_identity.py +0 -0
  22. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/bootstrap.py +0 -0
  23. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/__init__.py +0 -0
  24. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/api.py +0 -0
  25. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/browser_auth.py +0 -0
  26. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/cli.py +0 -0
  27. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/code_repository_status.py +0 -0
  28. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/config.py +0 -0
  29. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/docker_utils.py +0 -0
  30. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/doctor.py +0 -0
  31. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/local_ops.py +0 -0
  32. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/model_filters.py +0 -0
  33. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/pydantic_cli.py +0 -0
  34. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/sdk_utils.py +0 -0
  35. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/ssh_utils.py +0 -0
  36. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/cli/ui.py +0 -0
  37. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/__init__.py +0 -0
  38. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/_transport.py +0 -0
  39. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/base.py +0 -0
  40. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/client.py +0 -0
  41. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/compute_validation.py +0 -0
  42. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/exceptions.py +0 -0
  43. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/github_issues.py +0 -0
  44. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/models_foundry.py +0 -0
  45. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/models_helpers.py +0 -0
  46. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/models_user.py +0 -0
  47. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/observability.py +0 -0
  48. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/utils.py +0 -0
  49. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/client/value_sets.py +0 -0
  50. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/code_repository_context.py +0 -0
  51. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/code_repository_skills.py +0 -0
  52. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/defaults.py +0 -0
  53. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/instrumentation/__init__.py +0 -0
  54. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/instrumentation/utils.py +0 -0
  55. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/logconf.py +0 -0
  56. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/repository_identity_security.py +0 -0
  57. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/runtime_credential_exchange.py +0 -0
  58. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/runtime_flags.py +0 -0
  59. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/scaffold_skills.py +0 -0
  60. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/server/__init__.py +0 -0
  61. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/server/caller_assertions.py +0 -0
  62. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence/server/fastapi.py +0 -0
  63. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence.egg-info/SOURCES.txt +0 -0
  64. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence.egg-info/dependency_links.txt +0 -0
  65. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence.egg-info/entry_points.txt +0 -0
  66. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence.egg-info/requires.txt +0 -0
  67. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/mainsequence.egg-info/top_level.txt +0 -0
  68. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/setup.cfg +0 -0
  69. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_auth_bootstrap.py +0 -0
  70. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_auth_precedence.py +0 -0
  71. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_caller_assertions.py +0 -0
  72. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_canonical_api_route_contract.py +0 -0
  73. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_cli_auth_contract.py +0 -0
  74. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_cli_browser_auth.py +0 -0
  75. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_cli_secure_storage.py +0 -0
  76. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_client.py +0 -0
  77. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_code_repository_branch_contract.py +0 -0
  78. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_code_repository_documentation_contract.py +0 -0
  79. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_code_repository_runtime_context.py +0 -0
  80. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_code_repository_skills.py +0 -0
  81. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_credential_repr.py +0 -0
  82. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_dependency_extras.py +0 -0
  83. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_dev_release_version.py +0 -0
  84. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_environment_resource_context.py +0 -0
  85. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_environment_response_contracts.py +0 -0
  86. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_fastapi_request_identity.py +0 -0
  87. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_github_issues_client.py +0 -0
  88. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_http_retry_budget.py +0 -0
  89. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_independent_context.py +0 -0
  90. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_instrumentation.py +0 -0
  91. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_job_client.py +0 -0
  92. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_local_ops.py +0 -0
  93. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_logconf.py +0 -0
  94. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_models_user_request_bound_auth.py +0 -0
  95. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_notification_uid_contract.py +0 -0
  96. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_observability_client.py +0 -0
  97. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_runtime_credential_exchange.py +0 -0
  98. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_scaffold_skills.py +0 -0
  99. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_secret_client_model.py +0 -0
  100. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_source_ownership.py +0 -0
  101. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_ssh_utils.py +0 -0
  102. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_tolerant_response_reading.py +0 -0
  103. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_tolerant_value_sets.py +0 -0
  104. {mainsequence-9.0.9.dev46 → mainsequence-9.0.10.dev47}/tests/test_user_application_facts.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 9.0.9.dev46
3
+ Version: 9.0.10.dev47
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License-Expression: MIT
@@ -46,6 +46,21 @@ context. Never invent an Environment, use another branch as a fallback, or add
46
46
  inference environment variables. Provider secrets remain in existing server-side
47
47
  credential storage and must not appear in messages or `provider_options`.
48
48
 
49
+ ## Select a configured provider
50
+
51
+ Pass optional `custom_id="openai-work"` to `client.complete()` to select that named
52
+ credential within the caller's Environment. Omit it to retain the first available
53
+ owned credential, then the first shared credential. A missing or ambiguous explicit
54
+ name fails; do not retry using a different credential. Recorded requests retain the
55
+ exact credential UID for replay. Custom endpoints use their existing provider
56
+ identifier and do not accept `custom_id`.
57
+
58
+ Sharing a configured provider authorizes recipients, including workload users,
59
+ to use its credentials and receive them in local Tau. They may copy those credentials;
60
+ revoking a share cannot remove copies already delivered. Explain this before
61
+ guiding someone through sharing. This inference client executes on the server;
62
+ provider management remains with the existing provider clients.
63
+
49
64
  ## Construct a completion envelope
50
65
 
51
66
  Every call supplies complete ordered text messages: `system`, `user`, `assistant`.
@@ -727,6 +727,7 @@ class Agent(
727
727
  llm_provider: str | None = None,
728
728
  llm_model: str | None = None,
729
729
  llm_thinking: str | None = None,
730
+ custom_id: str | None = None,
730
731
  timeout=None,
731
732
  ) -> AgentSession:
732
733
  """
@@ -759,6 +760,10 @@ class Agent(
759
760
  "llm_provider": llm_provider,
760
761
  "llm_model": llm_model,
761
762
  "llm_thinking": llm_thinking,
763
+ "custom_id": (
764
+ custom_id
765
+ if custom_id is not None else None
766
+ ),
762
767
  }
763
768
  if resolved_session_uid and any(value is not None for value in creation_options.values()):
764
769
  raise ValueError("Creation options require handle_unique_id, not session_uid")
@@ -787,7 +792,14 @@ class Agent(
787
792
  session_payload = response.json()
788
793
  if not isinstance(session_payload, dict):
789
794
  raise TypeError("get_or_create_session response must be an AgentSession object")
790
- return AgentSession(**session_payload)
795
+ session = AgentSession(**session_payload)
796
+ if (
797
+ custom_id is not None
798
+ and session.custom_id
799
+ != creation_options["custom_id"]
800
+ ):
801
+ raise ValueError("The returned session does not use the requested configured provider")
802
+ return session
791
803
 
792
804
 
793
805
  class AgentSessionInsightsBase(BasePydanticModel):
@@ -1745,6 +1757,10 @@ class AgentSession(
1745
1757
  None,
1746
1758
  description="Timestamp when the session was archived.",
1747
1759
  )
1760
+ model_provider_credential_uid: str | None = None
1761
+ custom_id: str | None = Field(
1762
+ None, description="Resolved configured provider name; None for custom endpoints."
1763
+ )
1748
1764
  llm_provider: str = Field(
1749
1765
  ...,
1750
1766
  description="Resolved LLM provider actually used for this session. Unlike Agent defaults, this is intended to be the authoritative runtime record.",
@@ -20,6 +20,8 @@ class InferenceResult(BaseModel):
20
20
  sequence: int
21
21
  provider: str
22
22
  model: str
23
+ custom_id: str | None = None
24
+ model_provider_credential_uid: str | None = None
23
25
  status: str
24
26
  error_code: str
25
27
  content_available: bool
@@ -125,6 +127,7 @@ class InferenceClient:
125
127
  messages: list[dict[str, str]],
126
128
  idempotency_key: str,
127
129
  conversation_uid: str | UUID | None = None,
130
+ custom_id: str | None = None,
128
131
  thinking_level: str | None = None,
129
132
  provider_options: dict[str, Any] | None = None,
130
133
  provider_storage: dict[str, Any] | None = None,
@@ -142,6 +145,8 @@ class InferenceClient:
142
145
  "idempotency_key must contain 1–128 letters, digits, dots, underscores, colons or hyphens."
143
146
  )
144
147
  body = {"provider": provider, "model": model, "messages": deepcopy(messages)}
148
+ if custom_id is not None:
149
+ body["custom_id"] = custom_id
145
150
  if conversation_uid is not None:
146
151
  body["conversation_uid"] = str(UUID(str(conversation_uid)))
147
152
  for name, value in (
@@ -153,9 +158,15 @@ class InferenceClient:
153
158
  ):
154
159
  if value is not None:
155
160
  body[name] = deepcopy(value)
156
- return self._request(
161
+ result = self._request(
157
162
  "POST", "inference/completions/", body=body, key=idempotency_key, timeout=timeout
158
163
  )
164
+ if (
165
+ custom_id is not None
166
+ and result.custom_id != body["custom_id"]
167
+ ):
168
+ raise InferenceTransportError(idempotency_key)
169
+ return result
159
170
 
160
171
  def conversations(self, *, limit: int = 25, offset: int = 0) -> dict[str, Any]:
161
172
  return self._request(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 9.0.9.dev46
3
+ Version: 9.0.10.dev47
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mainsequence"
7
- version = "9.0.9.dev46"
7
+ version = "9.0.10.dev47"
8
8
  description = "Main Sequence SDK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -165,3 +165,33 @@ def test_invalid_success_is_uncertain_and_never_retried(transport, invalid):
165
165
  assert exc.value.idempotency_key == "stable"
166
166
  assert session.request.call_count == 1
167
167
  assert session.request.call_args.kwargs["allow_redirects"] is False
168
+
169
+
170
+ @pytest.mark.parametrize("subscription_uid", [None, "11111111-1111-4111-8111-111111111111"])
171
+ def test_completion_subscription_is_optional_and_preserves_request_identity(transport, subscription_uid):
172
+ session, response, _ = transport
173
+ response.json.return_value = result(custom_id=subscription_uid)
174
+ answer = inference.InferenceClient().complete(
175
+ provider="openai", model="configured-model", messages=[], idempotency_key="stable",
176
+ custom_id=subscription_uid,
177
+ )
178
+ body = session.request.call_args.kwargs["json"]
179
+ assert answer.custom_id == subscription_uid
180
+ assert session.request.call_args.kwargs["headers"]["Idempotency-Key"] == "stable"
181
+ if subscription_uid is None:
182
+ assert "custom_id" not in body
183
+ else:
184
+ assert body["custom_id"] == subscription_uid
185
+
186
+
187
+ @pytest.mark.parametrize("resolved", [None, "22222222-2222-4222-8222-222222222222"])
188
+ def test_completion_does_not_accept_ignored_or_different_subscription(transport, resolved):
189
+ session, response, _ = transport
190
+ response.json.return_value = result(custom_id=resolved)
191
+ with pytest.raises(inference.InferenceTransportError) as error:
192
+ inference.InferenceClient().complete(
193
+ provider="openai", model="configured-model", messages=[], idempotency_key="stable",
194
+ custom_id="11111111-1111-4111-8111-111111111111",
195
+ )
196
+ assert error.value.idempotency_key == "stable"
197
+ assert session.request.call_count == 1