Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9b6e4c0
Fix docs deploy: make main.py importable in the mkdocs container
v1r3n Aug 20, 2026
6ba9a5b
Add a prompt preview tab for LLM tasks in the agent execution view (#…
NicholasDCole Aug 21, 2026
b54f0d4
Docs refresh work (#1554)
nickorkes Aug 21, 2026
724fd33
ci: set org-level e2e server version on release (#1552)
kowser-orkes Aug 24, 2026
1444b62
https://orkes.atlassian.net/browse/CCOR-13360 (#1572)
ajlai-orkes Aug 26, 2026
0ff3884
https://orkes.atlassian.net/browse/CCOR-13363 (#1575)
ajlai-orkes Aug 27, 2026
a203b3e
fix(ui): escape non-ASCII chars in workflow save body to prevent WAF …
shaileshpadave Aug 28, 2026
14488d9
Add Human task manager role and Event handler manager role (#1580)
ajlai-orkes Aug 28, 2026
17260e0
Fix sync-execute param docs: remove nonexistent EVENTUAL value, docum…
nickorkes Aug 28, 2026
af8c6fa
Fix #1569 Postgres metadata status codes (#1571)
sergio-u-simspace Aug 28, 2026
1bda1d6
Make ES8 backwards compatible for introduction of classifier field (#…
rajeshwar-nu Aug 28, 2026
ce46eac
https://orkes.atlassian.net/browse/CCOR-13361 (#1581)
ajlai-orkes Aug 28, 2026
c4086f3
[CCOR-12995] Dedicated scheduler management role (#1588)
ajlai-orkes Aug 31, 2026
7c91005
[CCOR-13219] Add color for Queue Monitor role (#1593)
ajlai-orkes Sep 1, 2026
85e50c6
Docs: match Search executions to ui-next and document reasonForIncomp…
nickorkes Sep 2, 2026
467f72b
Fix flaky test (#1597)
ajlai-orkes Sep 2, 2026
75770d3
Add schema registry to OSS Conductor (#1592)
ambiorix2099 Sep 3, 2026
8d4c376
fix(deps): bump netty to 4.1.136.Final and micrometer to 1.15.12 (#15…
nthmost-orkes Sep 3, 2026
aaef68a
feat(redis-sentinel): add conductor.redis.sentinelPassword, no AUTH t…
jerrynieRe Sep 3, 2026
3627a16
Add size-based review-policy gate (extra approval for large PRs) (#1595)
nthmost-orkes Sep 4, 2026
ab305c8
review-policy: make label/comment writes best-effort so the gate neve…
nthmost-orkes Sep 4, 2026
30a103f
docs(ai): replace deprecated dall-e-3 with gpt-image-1 (#1512)
braxtonzhangio Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/generate_gh_pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CONFIG_FILE: mkdocs.yml
REQUIREMENTS: requirements.txt
# mkdocs.yml resolves `!!python/name:main.mermaid_fence` at config-parse
# time, so main.py must be importable. This action runs the `mkdocs`
# console script inside a container, where sys.path[0] is the script's
# bin dir rather than the checkout, and the repo is mounted here.
PYTHONPATH: /github/workspace
9 changes: 9 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ jobs:
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.SIGNING_KEY_ID }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }}
- name: Point SDK e2e workflows at this version
env:
GH_TOKEN: ${{ secrets.CI_UTIL_DISPATCH_TOKEN }}
run: |
export VERSION="${{github.ref_name}}"
export PUBLISH_VERSION=`echo ${VERSION:1}`
gh variable set CONDUCTOR_SERVER_VERSION \
--org conductor-oss \
--body "$PUBLISH_VERSION"

publish-docker:
runs-on: ubuntu-latest
Expand Down
211 changes: 211 additions & 0 deletions .github/workflows/review-policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
name: review-policy

# Size-based review policy: large PRs need an extra approval.
#
# This is a REVIEW-POLICY gate, deliberately kept separate from CI so its
# signal is never confused with a broken test:
# - The blocking check is named "review-policy / approvals" (not a test name).
# - The human-facing signal is a label + a sticky comment, so reviewers see
# the requirement in the labels/timeline, not only in the red checks row.
#
# Tiers:
# < 2000 changed lines -> 1 approval (enforced by native branch protection;
# this check stays green)
# >= 2000 changed lines -> 2 approvals (this check blocks until satisfied)
#
# Uses pull_request_target so the token is writable on fork PRs (needed to
# manage the label + comment). This workflow never checks out or executes PR
# code -- it only reads PR metadata via the API -- so pull_request_target is
# safe here.

on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
pull_request_review:
types: [submitted, dismissed]

permissions:
contents: read
pull-requests: write
issues: write

jobs:
approvals:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
script: |
const LARGE_PR_THRESHOLD = 2000; // changed lines (adds + dels)
const APPROVALS_FOR_LARGE = 2;
const APPROVALS_DEFAULT = 1;

const NEEDS_LABEL = `needs: ${APPROVALS_FOR_LARGE} approvals`;
const SIZE_LABEL = 'size: XL';
const COMMENT_MARKER = '<!-- review-policy:size-approvals -->';

// Files excluded from the line count: generated, vendored, binary,
// and lockfiles. Changes to these shouldn't push a PR into a higher
// review tier.
const EXCLUDE_EXACT = new Set([
'ui-next/pnpm-lock.yaml',
'ui/package-lock.json',
'ui/yarn.lock',
]);
const EXCLUDE_PREFIX = ['server/src/main/resources/swagger-ui/'];
const EXCLUDE_SUBSTR = ['/vendor/', '/generated/', '/__snapshots__/'];
const EXCLUDE_SUFFIX = [
'.lock', '.min.js', '.min.css', '.snap',
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.webp',
'.woff', '.woff2', '.ttf', '.eot', '.pdf',
];
const isExcluded = (f) =>
EXCLUDE_EXACT.has(f) ||
EXCLUDE_PREFIX.some((p) => f.startsWith(p)) ||
EXCLUDE_SUBSTR.some((s) => f.includes(s)) ||
EXCLUDE_SUFFIX.some((s) => f.endsWith(s));

const pr = context.payload.pull_request;
if (!pr) {
core.info('No pull_request in payload; nothing to check.');
return;
}
const repo = context.repo;
const num = pr.number;

// ---- helpers for the human-facing signal (label + sticky comment) ----
//
// The label + comment are a best-effort convenience layer. They need a
// GITHUB_TOKEN with write access, which isn't available when the repo's
// default workflow permission is read-only (Settings -> Actions ->
// Workflow permissions). The actual merge gate below is enforced purely
// by the check's pass/fail status, which needs NO write access -- so if
// a write is denied we log a warning and carry on rather than failing
// the whole job (and with it, unrelated PRs).
const warnSignal = (action, e) => {
const code = e && e.status ? `${e.status} ` : '';
core.warning(
`review-policy: skipped ${action} (${code}${(e && e.message) || e}). ` +
`The merge gate still works via the "review-policy / approvals" ` +
`check; the label/comment signal needs the workflow token to have ` +
`write access (repo Settings -> Actions -> Workflow permissions -> ` +
`"Read and write permissions").`
);
};
const ensureLabel = async (name, color, description) => {
try {
await github.rest.issues.createLabel({ ...repo, name, color, description });
} catch (e) {
if (e.status !== 422) throw e; // 422 == already exists
}
};
const addLabel = async (name) => {
try {
await ensureLabel(name, 'D93F0B', 'Applied by the review-policy workflow');
await github.rest.issues.addLabels({ ...repo, issue_number: num, labels: [name] });
} catch (e) {
warnSignal(`adding label "${name}"`, e);
}
};
const removeLabel = async (name) => {
try {
await github.rest.issues.removeLabel({ ...repo, issue_number: num, name });
} catch (e) {
if (e.status === 404) return; // 404 == label wasn't applied
warnSignal(`removing label "${name}"`, e);
}
};
const upsertComment = async (body) => {
try {
const marked = `${COMMENT_MARKER}\n${body}`;
const comments = await github.paginate(github.rest.issues.listComments, {
...repo, issue_number: num, per_page: 100,
});
const existing = comments.find((c) => c.body?.includes(COMMENT_MARKER));
if (existing) {
await github.rest.issues.updateComment({ ...repo, comment_id: existing.id, body: marked });
} else {
await github.rest.issues.createComment({ ...repo, issue_number: num, body: marked });
}
} catch (e) {
warnSignal('updating the status comment', e);
}
};

if (pr.draft) {
core.info('PR is a draft; clearing any policy signal and skipping.');
await removeLabel(NEEDS_LABEL);
return;
}

// ---- compute effective diff size ----
const files = await github.paginate(github.rest.pulls.listFiles, {
...repo, pull_number: num, per_page: 100,
});
let changed = 0;
const excluded = [];
for (const f of files) {
if (isExcluded(f.filename)) { excluded.push(f.filename); continue; }
changed += f.additions + f.deletions;
}
const changedStr = changed.toLocaleString('en-US');
core.info(`Changed lines (excluding generated/vendored): ${changed}`);
if (excluded.length) core.info(`Excluded ${excluded.length} file(s).`);

const required =
changed >= LARGE_PR_THRESHOLD ? APPROVALS_FOR_LARGE : APPROVALS_DEFAULT;

// Standard-size PR: the baseline approval is enforced by native branch
// protection (GitHub's "Review required" UI). Clear any policy signal
// this PR may have carried when it was larger, and pass green.
if (required <= APPROVALS_DEFAULT) {
core.info(
`Standard-size PR (< ${LARGE_PR_THRESHOLD} lines); ` +
`baseline of ${APPROVALS_DEFAULT} approval(s) handled by branch protection.`
);
await removeLabel(NEEDS_LABEL);
await removeLabel(SIZE_LABEL);
return;
}

// ---- large PR: count approvals (latest non-comment review per user) ----
const reviews = await github.paginate(github.rest.pulls.listReviews, {
...repo, pull_number: num, per_page: 100,
});
const latestByUser = new Map();
for (const r of reviews) {
if (r.state === 'COMMENTED') continue; // comments don't change standing
latestByUser.set(r.user.id, r.state);
}
const approvals = [...latestByUser.values()].filter((s) => s === 'APPROVED').length;
core.info(`Large PR requires ${required} approval(s); PR has ${approvals}.`);

await addLabel(SIZE_LABEL);

if (approvals >= required) {
await removeLabel(NEEDS_LABEL);
await upsertComment(
`👥 **Review policy — satisfied.** This PR changes **${changedStr}** ` +
`lines (≥ ${LARGE_PR_THRESHOLD.toLocaleString('en-US')}) and has the ` +
`required **${required}** approvals. ✅`
);
core.info('Approval requirement satisfied.');
return;
}

// Not enough approvals: raise the human-facing signal, then fail the
// (clearly-named) policy check to block merge.
await addLabel(NEEDS_LABEL);
const remaining = required - approvals;
await upsertComment(
`👥 **Review policy — action needed.** This PR changes **${changedStr}** ` +
`lines, which crosses the large-PR threshold ` +
`(${LARGE_PR_THRESHOLD.toLocaleString('en-US')}). Large PRs require ` +
`**${required}** approvals; it currently has **${approvals}**. ` +
`**${remaining}** more to go.\n\n` +
`_This is a review-policy gate, not a CI/test failure — see the ` +
`\`review-policy / approvals\` check._`
);
core.setFailed(
`Review policy: ${changedStr}-line PR needs ${required} approvals, has ${approvals}.`
);
14 changes: 7 additions & 7 deletions ai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ The Conductor AI module provides built-in integration with 13 popular LLM provid

| Provider | Chat | Embeddings | Image Gen | Audio Gen | Video Gen | Models |
|----------|:----:|:----------:|:---------:|:---------:|:---------:|--------|
| **OpenAI** | ✅ | ✅ | ✅ | ✅ | ✅ | GPT-4o, GPT-4o-mini, DALL-E-3, Sora-2, text-embedding-3-small/large |
| **OpenAI** | ✅ | ✅ | ✅ | ✅ | ✅ | GPT-4o, GPT-4o-mini, gpt-image-1, Sora-2, text-embedding-3-small/large |
| **Anthropic** | ✅ | ❌ | ❌ | ❌ | ❌ | Claude 3.5 Sonnet, Claude 3 Opus/Sonnet/Haiku, Claude 4 Sonnet |
| **Google Gemini** | ✅ | ✅ | ✅ | ✅ | ✅ | Gemini 2.5 Flash/Pro, Veo 2/3, Imagen, text-embedding-004 |
| **Azure OpenAI** | ✅ | ✅ | ✅ | ❌ | ❌ | GPT-4o, GPT-4, GPT-3.5-turbo, text-embedding-ada-002, DALL-E-3 |
| **Azure OpenAI** | ✅ | ✅ | ✅ | ❌ | ❌ | GPT-4o, GPT-4, GPT-3.5-turbo, text-embedding-ada-002, gpt-image-1 |
| **AWS Bedrock** | ✅ | ✅ | ❌ | ❌ | ❌ | Claude 3.x, Titan, Llama 3.x, amazon.titan-embed-text-v2:0 |
| **Mistral AI** | ✅ | ✅ | ❌ | ❌ | ❌ | Mistral Small/Medium/Large, Mixtral 8x7B, mistral-embed |
| **Cohere** | ✅ | ✅ | ❌ | ❌ | ❌ | Command, Command-R, Command-R+, embed-english-v3.0 |
Expand Down Expand Up @@ -154,7 +154,7 @@ Generate images from text prompts.
| Parameter | Type | Required | Description |
|-----------|------|:--------:|-------------|
| `llmProvider` | String | ✅ | Provider name (e.g., `openai`) |
| `model` | String | ✅ | Image model (e.g., `dall-e-3`) |
| `model` | String | ✅ | Image model (e.g., `gpt-image-1`) |
| `prompt` | String | ✅ | Image description |
| `width` | Integer | ❌ | Image width in pixels |
| `height` | Integer | ❌ | Image height in pixels |
Expand Down Expand Up @@ -231,7 +231,7 @@ Generate videos from text or image prompts. This is an **async task** -- it subm

**Provider-Specific Notes:**

- **OpenAI Sora**: Supports `sora-2` and `sora-2-pro` models. Valid durations are 4, 8, or 12 seconds. Valid sizes: `1280x720`, `720x1280`, `1792x1024`, `1024x1792`. Returns video + webp thumbnail.
- **OpenAI Sora**: Supports `sora-2` and `sora-2-pro` models. Valid durations are 4, 8, or 12 seconds. Valid sizes: `1280x720`, `720x1280`, `1536x1024`, `1024x1792`. Returns video + webp thumbnail.
- **Google Gemini Veo**: Supports `veo-2.0-generate-001`, `veo-3.0`, `veo-3.1`. Use `llmProvider` as `google_gemini` or `vertex_ai`. When using API key, no GCP credentials needed. Veo 3+ supports audio generation.

---
Expand Down Expand Up @@ -894,7 +894,7 @@ docker run -d \
"type": "GENERATE_IMAGE",
"inputParameters": {
"llmProvider": "openai",
"model": "dall-e-3",
"model": "gpt-image-1",
"prompt": "A futuristic cityscape at sunset",
"width": 1024,
"height": 1024,
Expand Down Expand Up @@ -1437,9 +1437,9 @@ A workflow that generates an image and a video in sequence:
"type": "GENERATE_IMAGE",
"inputParameters": {
"llmProvider": "openai",
"model": "dall-e-3",
"model": "gpt-image-1",
"prompt": "A serene mountain lake at dawn with mist rising from the water",
"width": 1792,
"width": 1536,
"height": 1024,
"n": 1
}
Expand Down
6 changes: 6 additions & 0 deletions ai/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ dependencies {
// supplied by the server at runtime.
compileOnly 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.springframework.boot:spring-boot-autoconfigure'
// SchemaValidationException, which this module catches, is a jakarta.validation.ValidationException.
compileOnly 'org.springframework.boot:spring-boot-starter-validation'

implementation project(':conductor-common')
implementation project(':conductor-core')
Expand Down Expand Up @@ -53,6 +55,10 @@ dependencies {


testImplementation 'org.springframework.boot:spring-boot-starter-web'
// The API only, not spring-boot-starter-validation: the starter puts hibernate-validator on
// the test classpath, which switches on Spring's method-validation proxying and breaks the
// A2A tests' mock verification. Catching SchemaValidationException only needs the class.
testImplementation 'jakarta.validation:jakarta.validation-api'
testImplementation "com.squareup.okhttp3:mockwebserver:4.12.0"
testImplementation "org.testcontainers:mongodb:${revTestContainer}"
testImplementation "org.testcontainers:postgresql:${revTestContainer}"
Expand Down
2 changes: 1 addition & 1 deletion ai/examples/03-image-generation.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"type": "GENERATE_IMAGE",
"inputParameters": {
"llmProvider": "openai",
"model": "dall-e-3",
"model": "gpt-image-1",
"prompt": "A futuristic cityscape at sunset",
"width": 1024,
"height": 1024,
Expand Down
6 changes: 3 additions & 3 deletions ai/examples/13-image-to-video-pipeline.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "image_to_video_pipeline",
"description": "A two-step creative pipeline: generates a still image with DALL-E, then creates a video continuation using OpenAI Sora with a prompt inspired by the scene.",
"description": "A two-step creative pipeline: generates a still image with gpt-image-1, then creates a video continuation using OpenAI Sora with a prompt inspired by the scene.",
"version": 1,
"schemaVersion": 2,
"tasks": [
Expand All @@ -10,9 +10,9 @@
"type": "GENERATE_IMAGE",
"inputParameters": {
"llmProvider": "openai",
"model": "dall-e-3",
"model": "gpt-image-1",
"prompt": "A serene mountain lake at dawn with mist rising from the water, photorealistic, wide landscape",
"width": 1792,
"width": 1536,
"height": 1024,
"n": 1
}
Expand Down
4 changes: 2 additions & 2 deletions ai/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ The server will be available at `http://localhost:3001/mcp`.
|------|-------------|--------------|
| `01-chat-completion.json` | Basic chat with GPT-4o-mini | OpenAI |
| `02-generate-embeddings.json` | Generate text embeddings | OpenAI |
| `03-image-generation.json` | Generate images with DALL-E 3 | OpenAI |
| `03-image-generation.json` | Generate images with gpt-image-1 | OpenAI |
| `04-audio-generation.json` | Text-to-speech with OpenAI TTS | OpenAI |
| `05-semantic-search.json` | Index and search documents | OpenAI, PostgreSQL |
| `06-rag-basic.json` | Basic RAG with search + answer | OpenAI/Anthropic, PostgreSQL |
Expand Down Expand Up @@ -342,7 +342,7 @@ curl -X POST 'http://localhost:8080/api/metadata/workflow' \
-H 'Content-Type: application/json' \
-d @13-image-to-video-pipeline.json

# Execute (generates a DALL-E image first, then a Sora video)
# Execute (generates a gpt-image-1 image first, then a Sora video)
curl -X POST 'http://localhost:8080/api/workflow/image_to_video_pipeline' \
-H 'Content-Type: application/json' \
-d '{}'
Expand Down
Loading
Loading