Skip to content

Commit 72c7937

Browse files
committed
feat(api): add delegation controls and prompt cache lifetimes
Castiron-Internal-PR: openai/openai-python-internal#334 Castiron-Source-SHA: f6ef0a7295ec69176d617ef519fbcd7177b843fd Castiron-Public-Base-SHA: d622f28
1 parent d622f28 commit 72c7937

95 files changed

Lines changed: 15382 additions & 8449 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.castiron.stats.yml‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
schema_version: 1
2-
generation_id: 592b2cad-c398-46a9-9df6-7858c09f3b65
3-
openapi_spec_hash: d6717da751735789c482f47d6613cea0
4-
openapi_transformed_spec_hash: 46e4f19c824b10d620e3c7bc4a7d569e
5-
config_hash: 0fe55890f9499fed76b60662804e30f7
6-
codegen_sha: f028b1b011d31cf0cd8a1939e83bf7ec5d2b8762
7-
codegen_hash: 2e485073ea1166bc85b3b8c04a512d221b82ef5c490801250ffc209d6292e201
8-
public_codegen_sha: b045ae7e94f055f952b812dfc4ddee314a691df7
2+
generation_id: 695a98fc-3b39-405a-ba6f-95697a31dde3
3+
openapi_spec_hash: 23f0bfa58ad53fef090bea6a72a05dda
4+
openapi_transformed_spec_hash: 39f2336a30336863627b56624b390f24
5+
config_hash: 249b5f41fe1b916955299cea75cddfbf
6+
codegen_sha: 39b02944c5834b9a41f695fd20748dc970c0535d
7+
codegen_hash: 6b670e1ce6b3a8f0ed1ebae40e404ef493708e0957bf01699d0fb361417fc0eb
8+
public_codegen_sha: d1ccd5a66720fd2cc5462bffc8b366f093ef8281

‎.github/workflows/castiron-custom-code-comment.yml‎

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,21 @@ jobs:
2929
pull-requests: read
3030
statuses: write
3131
steps:
32-
# Trusted main definition: no candidate checkout, artifacts, or executable inputs.
32+
# Execute only the helper belonging to this immutable workflow revision.
33+
- name: Check out the trusted review policy
34+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35+
with:
36+
ref: ${{ github.workflow_sha }}
37+
persist-credentials: false
38+
3339
- name: Re-evaluate current PR and queue after review changes
40+
env:
41+
GH_TOKEN: ${{ github.token }}
42+
GIT_CONFIG_COUNT: '2'
43+
GIT_CONFIG_KEY_0: credential.helper
44+
GIT_CONFIG_VALUE_0: ''
45+
GIT_CONFIG_KEY_1: credential.https://github.com.helper
46+
GIT_CONFIG_VALUE_1: '!gh auth git-credential'
3447
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
3548
with:
3649
script: |
@@ -48,12 +61,37 @@ jobs:
4861
run.head_sha !== signal.head_sha || run.path.split('@', 1)[0] !== '.github/workflows/castiron-custom-code.yml') {
4962
throw new Error('Unexpected or superseded review signal');
5063
}
64+
async function mergeBase(base, head) {
65+
const {data: comparison} = await github.rest.repos.compareCommitsWithBasehead({
66+
...context.repo, basehead: `${base}...${head}`, per_page: 1,
67+
});
68+
const sha = comparison.merge_base_commit?.sha;
69+
if (!/^[0-9a-f]{40}$/.test(sha || '')) throw new Error('Invalid policy merge base');
70+
return sha;
71+
}
72+
async function requiresHuman(pr, mainSha) {
73+
const path = require('node:path');
74+
const fs = require('node:fs');
75+
const root = fs.mkdtempSync(path.join(process.env.RUNNER_TEMP, 'castiron-review-'));
76+
try {
77+
const result = await exec.getExecOutput('python3', ['-I',
78+
'scripts/castiron/custom_code_policy.py', 'review-required',
79+
'--repository', repo, '--repo', path.join(root, 'objects.git'),
80+
'--base', pr.base.sha, '--head', pr.head.sha, '--main', mainSha], {silent: true});
81+
const decision = JSON.parse(result.stdout);
82+
if (typeof decision.requires_human !== 'boolean') throw new Error('Invalid review decision');
83+
return decision.requires_human;
84+
} finally {
85+
fs.rmSync(root, {recursive: true, force: true});
86+
}
87+
}
5188
const dirtyTargets = new Set();
5289
// Retry discovery within this job timeout while targets are changing.
5390
while (true) {
5491
const candidates = [];
5592
let pulls = run.pull_requests?.length ? run.pull_requests : await github.paginate(
5693
github.rest.repos.listPullRequestsAssociatedWithCommit, {...context.repo, commit_sha: run.head_sha});
94+
const hasAssociation = pulls.length > 0;
5795
if (!pulls.length) pulls = await github.paginate(github.rest.pulls.list, {
5896
...context.repo, state: 'open', head: `${run.head_repository.owner.login}:${run.head_branch}`,
5997
});
@@ -62,18 +100,41 @@ jobs:
62100
if (pr.state !== 'open' || pr.base.ref !== 'next' || pr.base.repo.full_name !== repo ||
63101
(item.head?.sha && pr.head.sha !== item.head.sha) || !/^[0-9a-f]{40}$/.test(pr.head.sha)) continue;
64102
if (run.head_sha !== pr.head.sha) {
65-
const {data: merge} = await github.rest.git.getRef({...context.repo, ref: `pull/${pr.number}/merge`});
66103
const {data: commit} = await github.rest.repos.getCommit({...context.repo, ref: run.head_sha});
67-
if (merge.object.sha !== run.head_sha || commit.parents.length !== 2 ||
68-
commit.parents[0].sha !== pr.base.sha || commit.parents[1].sha !== pr.head.sha) continue;
104+
if (commit.parents.length !== 2 || commit.parents[1].sha !== pr.head.sha ||
105+
!/^[0-9a-f]{40}$/.test(commit.parents[0].sha) ||
106+
await mergeBase(commit.parents[0].sha, pr.base.sha) !== commit.parents[0].sha) continue;
107+
// Without a commit association, the live merge ref must identify this PR.
108+
if (!hasAssociation) {
109+
const {data: merge} = await github.rest.git.getRef({...context.repo, ref: `pull/${pr.number}/merge`});
110+
if (merge.object.sha !== run.head_sha || commit.parents[0].sha !== pr.base.sha) continue;
111+
}
69112
}
70113
candidates.push(pr);
71114
}
72115
if (!candidates.length) return; // Main, closed, and superseded PR reviews have no next target.
73-
if (candidates.length !== 1) throw new Error('Review signal identifies ambiguous next PRs');
74-
const pr = candidates[0];
116+
// Commit associations may include other PRs sharing the same head.
117+
const matching = candidates.length === 1 ? candidates : candidates.filter(pr =>
118+
Number.isSafeInteger(run.head_repository?.id) && run.head_repository.id > 0 &&
119+
pr.head.repo?.id === run.head_repository.id && typeof run.head_branch === 'string' &&
120+
run.head_branch.length > 0 && pr.head.ref === run.head_branch);
121+
if (matching.length !== 1) throw new Error('Review signal identifies ambiguous next PRs');
122+
const pr = matching[0];
75123
const files = await github.paginate(github.rest.pulls.listFiles, {...context.repo, pull_number: pr.number});
76124
if (!files.some(file => file.filename === '.castiron-ratchet.json')) return;
125+
const {data: main} = await github.rest.git.getRef({...context.repo, ref: 'heads/main'});
126+
const mainSha = main.object.sha;
127+
const requiresReview = await requiresHuman(pr, mainSha);
128+
if (!requiresReview) {
129+
const [{data: current}, {data: target}, main] = await Promise.all([
130+
github.rest.pulls.get({...context.repo, pull_number: pr.number}),
131+
github.rest.git.getRef({...context.repo, ref: 'heads/next'}),
132+
mainSha && github.rest.git.getRef({...context.repo, ref: 'heads/main'}),
133+
]);
134+
if (current.head.sha !== pr.head.sha || current.base.sha !== pr.base.sha ||
135+
target.object.sha !== pr.base.sha || (main && main.data.object.sha !== mainSha)) continue;
136+
return;
137+
}
77138
// Read authoritative reviews, including edits: submittedAt alone misses
78139
// removal/restoration of an attestation. Never trust candidate event artifacts.
79140
let reviewCursor = null;

‎api.md‎

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,12 +257,13 @@ Methods:
257257
Types:
258258

259259
```python
260-
from openai.types.audio import Voice
260+
from openai.types.audio import Voice, VoiceDeleted
261261
```
262262

263263
Methods:
264264

265265
- <code title="post /audio/voices">client.audio.voices.<a href="./src/openai/resources/audio/voices.py">create</a>(\*\*<a href="src/openai/types/audio/voice_create_params.py">params</a>) -> <a href="./src/openai/types/audio/voice.py">Voice</a></code>
266+
- <code title="delete /audio/voices/{voice_id}">client.audio.voices.<a href="./src/openai/resources/audio/voices.py">delete</a>(voice_id) -> <a href="./src/openai/types/audio/voice_deleted.py">VoiceDeleted</a></code>
266267

267268
## VoiceConsents
268269

@@ -637,6 +638,49 @@ Methods:
637638
- <code title="get /agents">client.beta.agents.<a href="./src/openai/resources/beta/agents/agents.py">list</a>(\*\*<a href="src/openai/types/beta/agent_list_params.py">params</a>) -> <a href="./src/openai/types/beta/agent.py">SyncCursorPage[Agent]</a></code>
638639
- <code title="delete /agents/{agent_id}">client.beta.agents.<a href="./src/openai/resources/beta/agents/agents.py">delete</a>(agent_id) -> <a href="./src/openai/types/beta/agent_deleted.py">AgentDeleted</a></code>
639640

641+
### Automations
642+
643+
Types:
644+
645+
```python
646+
from openai.types.beta.agents import (
647+
Automation,
648+
AutomationAgentConfig,
649+
AutomationCreatedBy,
650+
AutomationDeleted,
651+
AutomationEnvironment,
652+
AutomationMetadataParam,
653+
AutomationTarget,
654+
AutomationTargetParam,
655+
OneTimeAutomation,
656+
PeriodicAutomation,
657+
)
658+
```
659+
660+
Methods:
661+
662+
- <code title="post /agents/automations">client.beta.agents.automations.<a href="./src/openai/resources/beta/agents/automations/automations.py">create</a>(\*\*<a href="src/openai/types/beta/agents/automation_create_params.py">params</a>) -> <a href="./src/openai/types/beta/agents/automation.py">Automation</a></code>
663+
- <code title="get /agents/automations/{automation_id}">client.beta.agents.automations.<a href="./src/openai/resources/beta/agents/automations/automations.py">retrieve</a>(automation_id) -> <a href="./src/openai/types/beta/agents/automation.py">Automation</a></code>
664+
- <code title="post /agents/automations/{automation_id}">client.beta.agents.automations.<a href="./src/openai/resources/beta/agents/automations/automations.py">update</a>(automation_id, \*\*<a href="src/openai/types/beta/agents/automation_update_params.py">params</a>) -> <a href="./src/openai/types/beta/agents/automation.py">Automation</a></code>
665+
- <code title="get /agents/automations">client.beta.agents.automations.<a href="./src/openai/resources/beta/agents/automations/automations.py">list</a>(\*\*<a href="src/openai/types/beta/agents/automation_list_params.py">params</a>) -> <a href="./src/openai/types/beta/agents/automation.py">SyncCursorPage[Automation]</a></code>
666+
- <code title="delete /agents/automations/{automation_id}">client.beta.agents.automations.<a href="./src/openai/resources/beta/agents/automations/automations.py">delete</a>(automation_id) -> <a href="./src/openai/types/beta/agents/automation_deleted.py">AutomationDeleted</a></code>
667+
- <code title="post /agents/automations/{automation_id}/pause">client.beta.agents.automations.<a href="./src/openai/resources/beta/agents/automations/automations.py">pause</a>(automation_id) -> <a href="./src/openai/types/beta/agents/automation.py">Automation</a></code>
668+
- <code title="post /agents/automations/{automation_id}/resume">client.beta.agents.automations.<a href="./src/openai/resources/beta/agents/automations/automations.py">resume</a>(automation_id) -> <a href="./src/openai/types/beta/agents/automation.py">Automation</a></code>
669+
670+
#### Invocations
671+
672+
Types:
673+
674+
```python
675+
from openai.types.beta.agents.automations import AutomationInvocation, AutomationInvocationSource
676+
```
677+
678+
Methods:
679+
680+
- <code title="post /agents/automations/{automation_id}/invocations">client.beta.agents.automations.invocations.<a href="./src/openai/resources/beta/agents/automations/invocations.py">create</a>(automation_id) -> <a href="./src/openai/types/beta/agents/automations/automation_invocation.py">AutomationInvocation</a></code>
681+
- <code title="get /agents/automations/{automation_id}/invocations/{invocation_id}">client.beta.agents.automations.invocations.<a href="./src/openai/resources/beta/agents/automations/invocations.py">retrieve</a>(invocation_id, \*, automation_id) -> <a href="./src/openai/types/beta/agents/automations/automation_invocation.py">AutomationInvocation</a></code>
682+
- <code title="get /agents/automations/{automation_id}/invocations">client.beta.agents.automations.invocations.<a href="./src/openai/resources/beta/agents/automations/invocations.py">list</a>(automation_id, \*\*<a href="src/openai/types/beta/agents/automations/invocation_list_params.py">params</a>) -> <a href="./src/openai/types/beta/agents/automations/automation_invocation.py">SyncCursorPage[AutomationInvocation]</a></code>
683+
640684
### Environments
641685

642686
Types:

0 commit comments

Comments
 (0)