Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 21 additions & 1 deletion .github/workflows/quality-gates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ jobs:
- name: Check workspace with all features
run: cargo check --workspace --all-features

- name: Verify Tauri injected-state IPC contracts
run: bash scripts/check-tauri-command-state-tests.sh

rust-tests:
name: Rust focused tests
runs-on: ubuntu-latest
Expand Down Expand Up @@ -245,6 +248,21 @@ jobs:
python3 -c 'import pathlib, sys; names = [line for line in pathlib.Path(sys.argv[1]).read_text().splitlines() if line.startswith("core_executor::tests::inference_tests::") and "rejects_contract_only_with_lifecycle" in line and line.endswith(": test")]; print("Contract-only lifecycle tests discovered:", len(names)); sys.exit(0 if names else 1)' "$RUNNER_TEMP/node-validation-lifecycle-tests.list"
cargo test -p node-engine --features inference-nodes --lib rejects_contract_only_with_lifecycle

- name: Run typed embedding capture regressions
run: |
set -euo pipefail
tests=(
core_executor::tests::inference_tests::test_canonical_llm_embedding_uses_typed_gateway_boundary
core_executor::tests::inference_tests::test_canonical_llm_embedding_with_package_facts_emits_compatibility_lifecycle
)
cargo test -p node-engine --features inference-nodes --lib test_canonical_llm_embedding -- --list > "$RUNNER_TEMP/node-embedding-tests.list"
for test_name in "${tests[@]}"; do
python3 -c 'import pathlib, sys; lines = pathlib.Path(sys.argv[1]).read_text().splitlines(); sys.exit(0 if sys.argv[2] + ": test" in lines else 1)' "$RUNNER_TEMP/node-embedding-tests.list" "$test_name"
result_path="$RUNNER_TEMP/node-embedding-${test_name##*::}.log"
cargo test -p node-engine --features inference-nodes --lib "$test_name" -- --exact 2>&1 | tee "$result_path"
python3 -c 'import pathlib, re, sys; text = pathlib.Path(sys.argv[1]).read_text(); sys.exit(0 if re.search(r"^test result: ok\. 1 passed; 0 failed; 0 ignored;", text, re.M) else 1)' "$result_path"
done

- name: Run node-engine tests
run: cargo test -p node-engine --lib

Expand Down Expand Up @@ -296,6 +314,8 @@ jobs:
cargo test -p pantograph-workflow-service --lib scheduler::task_orchestrator::tests::
- name: Run workflow-service contract tests
run: cargo test -p pantograph-workflow-service --test contract
- name: Run artifact-store integration contracts
run: cargo test -p pantograph-workflow-service --test artifact_store

rust-doc-tests:
name: Rust doc tests
Expand Down Expand Up @@ -441,7 +461,7 @@ jobs:
- name: Run warning-deny clippy audit
id: run-clippy-audit
continue-on-error: true
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
run: cargo clippy --workspace --all-targets --all-features --keep-going -- -D warnings

- name: Record clippy audit result
id: report-clippy-audit
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions bindings/csharp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ with any redistributed generator source/binary; this repository does not vendor
or package the generator itself. Existing generated-binding/artifact licensing
obligations remain unchanged.

## Shutdown result

Await `runtime.Shutdown()` and handle the generated `FfiException` if the
owned backend cannot stop. Its message contains the standard JSON error envelope
with `internal_error` and the original shutdown cause. A failed stop does not
mean the runtime released its residency; callers may retry after resolving the
cause. Existing C# await expressions remain valid, but regenerate bindings and
ship them with the matching native library when adopting this fallible API.
Rust callers now handle `Result<(), FfiError>` explicitly.

## Usage
Run the repository-level smoke script:

Expand Down
2 changes: 1 addition & 1 deletion crates/inference/src/resource_monitor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ mod tests {

#[test]
fn unsupported_resource_monitor_returns_typed_unavailable_observation() {
let monitor = unsupported::UnsupportedRuntimeResourceMonitor::default();
let monitor = unsupported::UnsupportedRuntimeResourceMonitor;
let guard = monitor
.start_process_monitor(std::process::id())
.expect("unsupported monitor starts");
Expand Down
5 changes: 4 additions & 1 deletion crates/node-engine/src/core_executor/inference_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3177,9 +3177,12 @@ impl InferenceBackend for MockTypedTextBackend {
}
}

#[cfg(feature = "inference-nodes")]
type CapturedEmbeddingRequest = (Vec<String>, String);

#[cfg(feature = "inference-nodes")]
struct MockTypedEmbeddingBackend {
embedding_requests: Arc<Mutex<Vec<(Vec<String>, String)>>>,
embedding_requests: Arc<Mutex<Vec<CapturedEmbeddingRequest>>>,
}

#[cfg(feature = "inference-nodes")]
Expand Down
2 changes: 1 addition & 1 deletion crates/node-engine/src/engine/dependency_inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ mod tests {
inputs.get("text"),
Some(&serde_json::json!("generated text"))
);
assert!(inputs.get("stream").is_none());
assert!(!inputs.contains_key("stream"));
}

#[test]
Expand Down
16 changes: 5 additions & 11 deletions crates/pantograph-diagnostics-ledger/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1571,12 +1571,9 @@ fn scheduler_timeline_projection_includes_inference_execution_diagnostics() {
assert!(detail.contains("cache handle observed"));
assert!(detail.contains("artifact refs 1"));
assert!(detail.contains("kv cache restore_input hit"));
assert_eq!(
record
.payload_json
.contains("generated text should not appear"),
false
);
assert!(!record
.payload_json
.contains("generated text should not appear"));
}

#[test]
Expand Down Expand Up @@ -5080,13 +5077,10 @@ fn sqlite_column_exists(conn: &Connection, table_name: &str, column_name: &str)
let mut stmt = conn
.prepare(&format!("PRAGMA table_info({table_name})"))
.expect("table info statement prepares");
let columns = stmt
let mut columns = stmt
.query_map([], |row| row.get::<_, String>(1))
.expect("table info query succeeds");
let exists = columns
.map(|column| column.expect("column row loads"))
.any(|column| column == column_name);
exists
columns.any(|column| column.expect("column row loads") == column_name)
}

fn assert_columns_exist(conn: &Connection, table_name: &str, column_names: &[&str]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async fn execute_data_graph_retired_onnx_audio_path_does_not_call_python_sidecar
.await
.expect("data graph execution");

assert!(outputs.get("audio").is_none());
assert!(!outputs.contains_key("audio"));
assert_eq!(
outputs.get("_graph_id"),
Some(&serde_json::json!("runtime-onnx-audio-data-graph"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ fn image_runtime_validation_snapshot(
workflow_semantic_version: version.semantic_version.clone(),
workflow_execution_fingerprint: version.execution_fingerprint.clone(),
descriptor_contract_version: INFERENCE_INTERFACE_CONTRACT_VERSION,
graph_revision: WorkflowGraphRevision::parse(&graph.compute_fingerprint())
graph_revision: WorkflowGraphRevision::parse(graph.compute_fingerprint())
.expect("valid graph revision"),
validation_session_id: DraftGraphValidationSessionId::parse(
"embedded_runtime_validation_session_1",
Expand Down
134 changes: 67 additions & 67 deletions crates/pantograph-embedded-runtime/src/node_io_artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,73 @@ fn node_io_artifact_format_metadata(media_type: &str) -> ArtifactFormatMetadata
}
}

fn io_artifact_payload_kind(kind: ArtifactPayloadKind) -> IoArtifactPayloadKind {
match kind {
ArtifactPayloadKind::Text => IoArtifactPayloadKind::Text,
ArtifactPayloadKind::Image => IoArtifactPayloadKind::Image,
ArtifactPayloadKind::Audio => IoArtifactPayloadKind::Audio,
ArtifactPayloadKind::Video => IoArtifactPayloadKind::Video,
ArtifactPayloadKind::ThreeD => IoArtifactPayloadKind::ThreeD,
ArtifactPayloadKind::LargeTable => IoArtifactPayloadKind::LargeTable,
ArtifactPayloadKind::GenericBinary => IoArtifactPayloadKind::GenericBinary,
ArtifactPayloadKind::Structured => IoArtifactPayloadKind::Structured,
}
}

fn io_artifact_lifecycle_state(state: ArtifactLifecycleState) -> IoArtifactLifecycleState {
match state {
ArtifactLifecycleState::Declared => IoArtifactLifecycleState::Declared,
ArtifactLifecycleState::Writing => IoArtifactLifecycleState::Writing,
ArtifactLifecycleState::Streaming => IoArtifactLifecycleState::Streaming,
ArtifactLifecycleState::Finalizing => IoArtifactLifecycleState::Finalizing,
ArtifactLifecycleState::Retained => IoArtifactLifecycleState::Retained,
ArtifactLifecycleState::Failed => IoArtifactLifecycleState::Failed,
ArtifactLifecycleState::Expired => IoArtifactLifecycleState::Expired,
ArtifactLifecycleState::Deleted => IoArtifactLifecycleState::Deleted,
}
}

fn io_artifact_access_mode(mode: ArtifactAccessMode) -> IoArtifactAccessMode {
match mode {
ArtifactAccessMode::Read => IoArtifactAccessMode::Read,
ArtifactAccessMode::Download => IoArtifactAccessMode::Download,
ArtifactAccessMode::Stream => IoArtifactAccessMode::Stream,
}
}

fn io_artifact_format_metadata(format: ArtifactFormatMetadata) -> IoArtifactFormatMetadata {
IoArtifactFormatMetadata {
format_id: format.format_id,
media_type: format.media_type,
codec_id: format.codec_id,
quality_percent: format.quality_percent,
bitrate_kbps: format.bitrate_kbps,
crf: format.crf,
bit_depth: format.bit_depth,
color_profile_id: format.color_profile_id,
converter_id: format.converter_id,
converter_version: format.converter_version,
library_version: format.library_version,
conversion_id: format.conversion_id,
conversion_status: format.conversion_status.map(|status| match status {
ArtifactConversionStatus::Converted => IoArtifactConversionStatus::Converted,
ArtifactConversionStatus::PassedThrough => IoArtifactConversionStatus::PassedThrough,
ArtifactConversionStatus::Failed => IoArtifactConversionStatus::Failed,
}),
conversion_command_id: format.conversion_command_id,
conversion_dependencies: format
.conversion_dependencies
.into_iter()
.map(|dependency| IoArtifactConversionDependency {
dependency_id: dependency.dependency_id,
active_version: dependency.active_version,
lease_id: dependency.lease_id,
lease_holder: dependency.lease_holder,
})
.collect(),
}
}

#[cfg(test)]
mod tests {
use super::{
Expand Down Expand Up @@ -394,70 +461,3 @@ mod tests {
);
}
}

fn io_artifact_payload_kind(kind: ArtifactPayloadKind) -> IoArtifactPayloadKind {
match kind {
ArtifactPayloadKind::Text => IoArtifactPayloadKind::Text,
ArtifactPayloadKind::Image => IoArtifactPayloadKind::Image,
ArtifactPayloadKind::Audio => IoArtifactPayloadKind::Audio,
ArtifactPayloadKind::Video => IoArtifactPayloadKind::Video,
ArtifactPayloadKind::ThreeD => IoArtifactPayloadKind::ThreeD,
ArtifactPayloadKind::LargeTable => IoArtifactPayloadKind::LargeTable,
ArtifactPayloadKind::GenericBinary => IoArtifactPayloadKind::GenericBinary,
ArtifactPayloadKind::Structured => IoArtifactPayloadKind::Structured,
}
}

fn io_artifact_lifecycle_state(state: ArtifactLifecycleState) -> IoArtifactLifecycleState {
match state {
ArtifactLifecycleState::Declared => IoArtifactLifecycleState::Declared,
ArtifactLifecycleState::Writing => IoArtifactLifecycleState::Writing,
ArtifactLifecycleState::Streaming => IoArtifactLifecycleState::Streaming,
ArtifactLifecycleState::Finalizing => IoArtifactLifecycleState::Finalizing,
ArtifactLifecycleState::Retained => IoArtifactLifecycleState::Retained,
ArtifactLifecycleState::Failed => IoArtifactLifecycleState::Failed,
ArtifactLifecycleState::Expired => IoArtifactLifecycleState::Expired,
ArtifactLifecycleState::Deleted => IoArtifactLifecycleState::Deleted,
}
}

fn io_artifact_access_mode(mode: ArtifactAccessMode) -> IoArtifactAccessMode {
match mode {
ArtifactAccessMode::Read => IoArtifactAccessMode::Read,
ArtifactAccessMode::Download => IoArtifactAccessMode::Download,
ArtifactAccessMode::Stream => IoArtifactAccessMode::Stream,
}
}

fn io_artifact_format_metadata(format: ArtifactFormatMetadata) -> IoArtifactFormatMetadata {
IoArtifactFormatMetadata {
format_id: format.format_id,
media_type: format.media_type,
codec_id: format.codec_id,
quality_percent: format.quality_percent,
bitrate_kbps: format.bitrate_kbps,
crf: format.crf,
bit_depth: format.bit_depth,
color_profile_id: format.color_profile_id,
converter_id: format.converter_id,
converter_version: format.converter_version,
library_version: format.library_version,
conversion_id: format.conversion_id,
conversion_status: format.conversion_status.map(|status| match status {
ArtifactConversionStatus::Converted => IoArtifactConversionStatus::Converted,
ArtifactConversionStatus::PassedThrough => IoArtifactConversionStatus::PassedThrough,
ArtifactConversionStatus::Failed => IoArtifactConversionStatus::Failed,
}),
conversion_command_id: format.conversion_command_id,
conversion_dependencies: format
.conversion_dependencies
.into_iter()
.map(|dependency| IoArtifactConversionDependency {
dependency_id: dependency.dependency_id,
active_version: dependency.active_version,
lease_id: dependency.lease_id,
lease_holder: dependency.lease_holder,
})
.collect(),
}
}
Loading
Loading