mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
## What changed - Support `model_provider` and `model_providers` in managed requirements. Required selection overrides local and session configuration; each required provider definition replaces the corresponding local entry, including its authentication and headers. - Merge provider requirement fragments before validation, preserving source-relative `auth.cwd` paths. Reject invalid cloud provider definitions before replacing the cached bundle, including unsupported Bedrock overrides. - Expose `modelProvider` and `modelProviders` through the configuration requirements API and generated schemas. Reject writes to managed provider settings and omit their local origins, including for provider IDs containing dots. ## Testing Add tests for requirement precedence, authentication fragment merging, path resolution, cloud cache preservation, and read-only configuration RPCs. Integration tests verify that model discovery and inference use the required gateway and ChatGPT authentication without leaking local headers, and that provider requirements survive configuration rebuilds. GitOrigin-RevId: d1448604d4ef5662ebb5df7b693712d3b8ad6d54
85 lines
2.7 KiB
Rust
85 lines
2.7 KiB
Rust
use crate::ConfigLayerMetadata;
|
|
use crate::merge::is_structured_feature_path;
|
|
use serde_json::Value as JsonValue;
|
|
use sha2::Digest;
|
|
use sha2::Sha256;
|
|
use std::collections::HashMap;
|
|
use toml::Value as TomlValue;
|
|
|
|
pub(super) fn record_origins(
|
|
value: &TomlValue,
|
|
meta: &ConfigLayerMetadata,
|
|
path: &mut Vec<String>,
|
|
origins: &mut HashMap<String, ConfigLayerMetadata>,
|
|
include: &impl Fn(&[String]) -> bool,
|
|
) {
|
|
match value {
|
|
TomlValue::Table(table) => {
|
|
for (key, val) in table {
|
|
path.push(key.clone());
|
|
record_origins(val, meta, path, origins, include);
|
|
path.pop();
|
|
}
|
|
}
|
|
TomlValue::Array(items) => {
|
|
for (idx, item) in (0_i32..).zip(items.iter()) {
|
|
path.push(idx.to_string());
|
|
record_origins(item, meta, path, origins, include);
|
|
path.pop();
|
|
}
|
|
}
|
|
_ => {
|
|
if !path.is_empty() {
|
|
if !include(path) {
|
|
return;
|
|
}
|
|
if matches!(value, TomlValue::Boolean(_)) && is_structured_feature_path(path) {
|
|
if path
|
|
.last()
|
|
.is_some_and(|feature| feature == "network_proxy")
|
|
{
|
|
origins.insert(path.join("."), meta.clone());
|
|
}
|
|
path.push("enabled".to_string());
|
|
origins.insert(path.join("."), meta.clone());
|
|
path.pop();
|
|
return;
|
|
}
|
|
origins.insert(path.join("."), meta.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn version_for_toml(value: &TomlValue) -> String {
|
|
let json = serde_json::to_value(value).unwrap_or(JsonValue::Null);
|
|
let canonical = canonical_json(&json);
|
|
let serialized = serde_json::to_vec(&canonical).unwrap_or_default();
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(serialized);
|
|
let hash = hasher.finalize();
|
|
let hex = hash
|
|
.iter()
|
|
.map(|byte| format!("{byte:02x}"))
|
|
.collect::<String>();
|
|
format!("sha256:{hex}")
|
|
}
|
|
|
|
fn canonical_json(value: &JsonValue) -> JsonValue {
|
|
match value {
|
|
JsonValue::Object(map) => {
|
|
let mut sorted = serde_json::Map::new();
|
|
let mut keys = map.keys().cloned().collect::<Vec<_>>();
|
|
keys.sort();
|
|
for key in keys {
|
|
if let Some(val) = map.get(&key) {
|
|
sorted.insert(key, canonical_json(val));
|
|
}
|
|
}
|
|
JsonValue::Object(sorted)
|
|
}
|
|
JsonValue::Array(items) => JsonValue::Array(items.iter().map(canonical_json).collect()),
|
|
other => other.clone(),
|
|
}
|
|
}
|