imbi-plugin-openai 2.44.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.
@@ -0,0 +1,8 @@
1
+ """Imbi OpenAI plugin: workload identity federation."""
2
+
3
+ from imbi.plugins.openai.plugin import OpenAIPlugin
4
+ from imbi.plugins.openai.wif import OpenAIExchanger, OpenAIWif
5
+
6
+ PLUGIN = OpenAIPlugin
7
+
8
+ __all__ = ['PLUGIN', 'OpenAIExchanger', 'OpenAIPlugin', 'OpenAIWif']
@@ -0,0 +1,21 @@
1
+ """The OpenAI plugin manifest.
2
+
3
+ The plugin has no Integration capability. It adds the ``openai_wif``
4
+ secret provider and the OpenAI token exchanger (for AI Providers).
5
+ """
6
+
7
+ import imbi.common.plugins as plugins
8
+ from imbi.plugins.openai import wif
9
+
10
+
11
+ class OpenAIPlugin(plugins.Plugin):
12
+ manifest = plugins.PluginManifest(
13
+ slug='openai',
14
+ name='OpenAI',
15
+ description='OpenAI workload identity federation for model calls',
16
+ icon='si-openai',
17
+ auth_type='none',
18
+ capabilities=[],
19
+ secret_providers=[wif.OpenAIWif],
20
+ token_exchangers=[wif.OpenAIExchanger],
21
+ )
File without changes
@@ -0,0 +1,83 @@
1
+ """OpenAI workload identity federation (WIF plan 3.3, 4.3, 4.5).
2
+
3
+ - :class:`OpenAIExchanger` exchanges an identity JWT for an OpenAI
4
+ access token at a fixed endpoint (D8), with an RFC 8693 token
5
+ exchange.
6
+ - :class:`OpenAIWif` is the ``openai_wif`` secret provider. A dynamic
7
+ binding holds the exchanger target. Each resolution mints a new token
8
+ for the agent of the task through core (``vendor_token``).
9
+
10
+ The subject token and the access token are never logged.
11
+ """
12
+
13
+ import typing
14
+
15
+ import pydantic
16
+
17
+ from imbi.common.plugins import identity
18
+
19
+ #: Fixed in code (D8). No tenant setting can change it.
20
+ TOKEN_URL = 'https://auth.openai.com/oauth/token'
21
+ _GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:token-exchange'
22
+ _SUBJECT_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:jwt'
23
+
24
+ _Id = typing.Annotated[
25
+ str,
26
+ pydantic.StringConstraints(
27
+ min_length=1, max_length=128, pattern=r'^[A-Za-z0-9_-]+$'
28
+ ),
29
+ ]
30
+
31
+
32
+ class _Target(pydantic.BaseModel):
33
+ """The OpenAI ids of one workload identity provider and account."""
34
+
35
+ model_config = pydantic.ConfigDict(extra='forbid')
36
+
37
+ identity_provider_id: _Id
38
+ service_account_id: _Id
39
+
40
+
41
+ class OpenAIExchanger(identity.TokenExchanger):
42
+ vendor = 'openai'
43
+ label = 'OpenAI'
44
+ target_help = (
45
+ f'{identity.target_keys(_Target)}, the ids from the OpenAI dashboard.'
46
+ )
47
+ #: The audience of the OpenAI workload identity provider. The
48
+ #: provider in the OpenAI dashboard must use this value.
49
+ audience = 'https://api.openai.com/v1'
50
+ #: An OpenAI token never outlives its subject token. 3300 seconds
51
+ #: keeps the ``aws_sts`` role session at 3600 seconds, the AWS
52
+ #: limit for role chaining (imbi-api on IRSA or EKS Pod Identity).
53
+ jwt_seconds = 3300
54
+
55
+ def validate_target(
56
+ self, target: dict[str, typing.Any]
57
+ ) -> dict[str, typing.Any]:
58
+ return identity.validate_model(_Target, target)
59
+
60
+ async def exchange(
61
+ self, target: dict[str, typing.Any], identity_token: str
62
+ ) -> identity.AccessToken:
63
+ return await identity.post_exchange(
64
+ 'OpenAI',
65
+ TOKEN_URL,
66
+ {
67
+ 'grant_type': _GRANT_TYPE,
68
+ 'subject_token': identity_token,
69
+ 'subject_token_type': _SUBJECT_TOKEN_TYPE,
70
+ **target,
71
+ },
72
+ )
73
+
74
+
75
+ EXCHANGER = OpenAIExchanger()
76
+
77
+
78
+ class OpenAIWif(identity.WifSecretProvider):
79
+ """``openai_wif``: an OpenAI token for the agent of the task."""
80
+
81
+ type = 'openai_wif'
82
+ label = 'OpenAI workload identity'
83
+ exchanger = EXCHANGER
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.5
2
+ Name: imbi-plugin-openai
3
+ Version: 2.44.0
4
+ Summary: OpenAI workload identity federation for Imbi
5
+ Author-email: "Gavin M. Roy" <gavinr@aweber.com>
6
+ License-Expression: BSD-3-Clause
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Natural Language :: English
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Python: >=3.14
14
+ Requires-Dist: imbi-common==2.44.0
15
+ Requires-Dist: pydantic>=2
16
+ Description-Content-Type: text/markdown
17
+
18
+ # imbi-plugin-openai
19
+
20
+ OpenAI workload identity federation (WIF) for Imbi. The plugin adds:
21
+
22
+ - The OpenAI token exchanger. An OpenAI AI Provider can then use workload
23
+ identity instead of a stored key (Admin, AI Models).
24
+ - The `openai_wif` secret provider. A dynamic binding then mints a
25
+ short-lived OpenAI token for the agent of the task. The harness does
26
+ not use it yet: the Claude Agent SDK calls only the Anthropic API.
27
+
28
+ ## How it works
29
+
30
+ 1. The organization selects an identity source (for example `aws_sts`) in
31
+ Admin, Secrets, Identity.
32
+ 2. In the OpenAI dashboard, create a workload identity provider for the
33
+ issuer of that identity source. Set its audience to
34
+ `https://api.openai.com/v1`. Imbi always requests this audience.
35
+ See the [OpenAI guide](https://developers.openai.com/api/reference/workload-identity-federation).
36
+ 3. Create a service account and a mapping. The mapping matches the role
37
+ ARN (`sub`) and the request tag `imbi_ai_provider` (an AI Provider) or
38
+ `imbi_agent` (an agent). AWS request tags are nested in the JWT, so
39
+ expose them with a CEL transformation, for example
40
+ `assertion["https://sts.amazonaws.com/"]["request_tags"]["imbi_ai_provider"]`.
41
+ 4. Give Imbi the ids: on the AI Provider (Admin, AI Models, the key icon)
42
+ or in a dynamic `openai_wif` binding.
43
+ 5. At use time, Imbi gets a new identity JWT that lasts 55 minutes, and
44
+ exchanges it at `https://auth.openai.com/oauth/token` (RFC 8693). The
45
+ token never outlives the JWT.
46
+
47
+ ## Target
48
+
49
+ | Field | Required | Description |
50
+ | ---------------------- | -------- | ----------------------------------------- |
51
+ | `identity_provider_id` | yes | The OpenAI workload identity provider id. |
52
+ | `service_account_id` | yes | The OpenAI service account id. |
53
+
54
+ The provider takes no configuration.
@@ -0,0 +1,7 @@
1
+ imbi/plugins/openai/__init__.py,sha256=AF8RgSyQ-1_wxySgEUHsOyNPFNCzaWwot_-J7DQpAfc,265
2
+ imbi/plugins/openai/plugin.py,sha256=vNGYtnSJ242FaFMtrQ0dNZrHKyFeeOaEjSy7V1dbMIQ,618
3
+ imbi/plugins/openai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ imbi/plugins/openai/wif.py,sha256=Gvf-k3j8WZrZxt_9R3UDZ7Jp7mBIzetjD3lbkjC51-w,2567
5
+ imbi_plugin_openai-2.44.0.dist-info/METADATA,sha256=5ESav64AQ8uoe6CnQWGu0mfSwj3_i0BL5IeRC-eWAEE,2469
6
+ imbi_plugin_openai-2.44.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
7
+ imbi_plugin_openai-2.44.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any