Add Codex auto-review model metadata
This commit is contained in:
1 parent
84eb26ad89
commit
8a9c133c0e
3 files changed
+170
-32
No files matched your search
@@ -142,7 +142,7 @@ nonisolated enum ModelCapabilityResolver {
|
||||
private static func fill(
|
||||
_ model: AIModel,
|
||||
reported: ReportedFields,
|
||||
from record: PiModelRecord
|
||||
from record: CatalogRecord
|
||||
) -> AIModel {
|
||||
var result = model
|
||||
if result.displayName.isEmpty { result.displayName = record.name }
|
||||
@@ -152,23 +152,35 @@ nonisolated enum ModelCapabilityResolver {
|
||||
let modalities = Set(record.input.compactMap(AIModel.Modality.init(rawValue:)))
|
||||
result.input = modalities.isEmpty ? [.text] : modalities
|
||||
}
|
||||
if !reported.contains(.output) { result.output = [.text] }
|
||||
if !reported.contains(.output) {
|
||||
let modalities = Set(
|
||||
(record.output ?? [AIModel.Modality.text.rawValue])
|
||||
.compactMap(AIModel.Modality.init(rawValue:))
|
||||
)
|
||||
result.output = modalities.isEmpty ? [.text] : modalities
|
||||
}
|
||||
if !reported.contains(.abilities) {
|
||||
// Pi's language-model catalog is consumed by an agent runtime and
|
||||
// its provider APIs accept tool schemas. `reasoning` is the one
|
||||
// model-specific ability Pi records explicitly.
|
||||
result.abilities = [.toolCall]
|
||||
if record.reasoning { result.abilities.insert(.reasoning) }
|
||||
if let abilities = record.abilities {
|
||||
result.abilities = Set(abilities.compactMap(AIModel.Ability.init(rawValue:)))
|
||||
} else {
|
||||
// Pi's language-model catalog is consumed by an agent runtime and
|
||||
// its provider APIs accept tool schemas. `reasoning` is the one
|
||||
// model-specific ability Pi records explicitly.
|
||||
result.abilities = [.toolCall]
|
||||
if record.reasoning { result.abilities.insert(.reasoning) }
|
||||
}
|
||||
}
|
||||
if result.contextLength == nil { result.contextLength = record.contextWindow }
|
||||
if result.maxOutputTokens == nil { result.maxOutputTokens = record.maxOutputTokens }
|
||||
if result.maxOutputTokens == nil, let maxOutputTokens = record.maxOutputTokens {
|
||||
result.maxOutputTokens = maxOutputTokens
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private static func reasoning(
|
||||
model: AIModel,
|
||||
provider: ModelProvider,
|
||||
record: PiModelRecord?
|
||||
record: CatalogRecord?
|
||||
) -> ReasoningResolution {
|
||||
guard model.abilities.contains(.reasoning) else {
|
||||
return ReasoningResolution(supported: [.off])
|
||||
@@ -198,7 +210,7 @@ nonisolated enum ModelCapabilityResolver {
|
||||
}
|
||||
|
||||
private static func automaticReasoning(
|
||||
record: PiModelRecord?,
|
||||
record: CatalogRecord?,
|
||||
provider: ModelProvider
|
||||
) -> ReasoningResolution {
|
||||
guard let record, record.reasoning else {
|
||||
@@ -209,13 +221,21 @@ nonisolated enum ModelCapabilityResolver {
|
||||
return ReasoningResolution(supported: supported, wireValues: values)
|
||||
}
|
||||
|
||||
let unsupported = Set(record.unsupportedEfforts.compactMap(ReasoningEffort.init(piName:)))
|
||||
var supported = ReasoningEffort.allCases.filter { effort in
|
||||
if unsupported.contains(effort) { return false }
|
||||
if effort == .xHigh || effort == .max {
|
||||
return record.effortWireValues[effort.piName] != nil
|
||||
var supported: [ReasoningEffort]
|
||||
if let declared = record.supportedEfforts {
|
||||
let declaredSet = Set(declared.compactMap(ReasoningEffort.init(piName:)))
|
||||
supported = ReasoningEffort.allCases.filter(declaredSet.contains)
|
||||
} else {
|
||||
let unsupported = Set(
|
||||
(record.unsupportedEfforts ?? []).compactMap(ReasoningEffort.init(piName:))
|
||||
)
|
||||
supported = ReasoningEffort.allCases.filter { effort in
|
||||
if unsupported.contains(effort) { return false }
|
||||
if effort == .xHigh || effort == .max {
|
||||
return record.effortWireValues[effort.piName] != nil
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
if supported.isEmpty { supported = [.off] }
|
||||
|
||||
@@ -240,13 +260,13 @@ nonisolated enum ModelCapabilityResolver {
|
||||
private static let catalog = PiModelCatalog.load()
|
||||
|
||||
private struct PiModelCatalog: Sendable {
|
||||
var recordsByKey: [String: PiModelRecord]
|
||||
var recordsByID: [String: [PiModelRecord]]
|
||||
var recordsByKey: [String: CatalogRecord]
|
||||
var recordsByID: [String: [CatalogRecord]]
|
||||
var metadata: CatalogMetadata?
|
||||
|
||||
static func load() -> PiModelCatalog {
|
||||
let bundles = [Bundle.main, Bundle(for: BundleToken.self)]
|
||||
let url = bundles.lazy.compactMap {
|
||||
let piURL = bundles.lazy.compactMap {
|
||||
$0.url(forResource: "PiModelCatalog", withExtension: "json")
|
||||
?? $0.url(
|
||||
forResource: "PiModelCatalog",
|
||||
@@ -254,18 +274,47 @@ nonisolated enum ModelCapabilityResolver {
|
||||
subdirectory: "Resources"
|
||||
)
|
||||
}.first
|
||||
guard let url,
|
||||
let data = try? Data(contentsOf: url),
|
||||
guard let piURL,
|
||||
let data = try? Data(contentsOf: piURL),
|
||||
let payload = try? JSONDecoder().decode(PiCatalogPayload.self, from: data)
|
||||
else {
|
||||
return PiModelCatalog(recordsByKey: [:], recordsByID: [:], metadata: nil)
|
||||
}
|
||||
|
||||
var byKey: [String: PiModelRecord] = [:]
|
||||
var byID: [String: [PiModelRecord]] = [:]
|
||||
var byKey: [String: CatalogRecord] = [:]
|
||||
var byID: [String: [CatalogRecord]] = [:]
|
||||
func upsert(_ record: CatalogRecord) {
|
||||
let recordKey = key(provider: record.provider, modelID: record.id)
|
||||
let normalizedID = normalized(record.id)
|
||||
byKey[recordKey] = record
|
||||
byID[normalizedID, default: []].removeAll {
|
||||
key(provider: $0.provider, modelID: $0.id) == recordKey
|
||||
}
|
||||
byID[normalizedID, default: []].append(record)
|
||||
}
|
||||
|
||||
for record in payload.models {
|
||||
byKey[key(provider: record.provider, modelID: record.id)] = record
|
||||
byID[record.id.lowercased(), default: []].append(record)
|
||||
upsert(record)
|
||||
}
|
||||
|
||||
let overridesURL = bundles.lazy.compactMap {
|
||||
$0.url(forResource: "ModelCatalogOverrides", withExtension: "json")
|
||||
?? $0.url(
|
||||
forResource: "ModelCatalogOverrides",
|
||||
withExtension: "json",
|
||||
subdirectory: "Resources"
|
||||
)
|
||||
}.first
|
||||
if let overridesURL,
|
||||
let overridesData = try? Data(contentsOf: overridesURL),
|
||||
let overrides = try? JSONDecoder().decode(
|
||||
CatalogOverridesPayload.self,
|
||||
from: overridesData
|
||||
)
|
||||
{
|
||||
for record in overrides.models {
|
||||
upsert(record)
|
||||
}
|
||||
}
|
||||
return PiModelCatalog(
|
||||
recordsByKey: byKey,
|
||||
@@ -279,7 +328,7 @@ nonisolated enum ModelCapabilityResolver {
|
||||
)
|
||||
}
|
||||
|
||||
func record(modelID: String, provider: ModelProvider) -> PiModelRecord? {
|
||||
func record(modelID: String, provider: ModelProvider) -> CatalogRecord? {
|
||||
let normalizedID = Self.normalized(modelID)
|
||||
let preferredProvider = Self.piProviderID(for: provider)
|
||||
|
||||
@@ -302,9 +351,9 @@ nonisolated enum ModelCapabilityResolver {
|
||||
}
|
||||
|
||||
private static func directVendorRecord(
|
||||
in records: [PiModelRecord],
|
||||
in records: [CatalogRecord],
|
||||
modelID: String
|
||||
) -> PiModelRecord? {
|
||||
) -> CatalogRecord? {
|
||||
let namespace = modelID.split(separator: "/", maxSplits: 1).first.map(String.init)
|
||||
let aliases: [String: String] = [
|
||||
"openai": "openai",
|
||||
@@ -358,18 +407,25 @@ nonisolated enum ModelCapabilityResolver {
|
||||
var package: String
|
||||
var version: String
|
||||
var commit: String
|
||||
var models: [PiModelRecord]
|
||||
var models: [CatalogRecord]
|
||||
}
|
||||
|
||||
private struct PiModelRecord: Decodable, Sendable {
|
||||
private struct CatalogOverridesPayload: Decodable, Sendable {
|
||||
var models: [CatalogRecord]
|
||||
}
|
||||
|
||||
private struct CatalogRecord: Decodable, Sendable {
|
||||
var provider: String
|
||||
var id: String
|
||||
var name: String
|
||||
var reasoning: Bool
|
||||
var input: [String]
|
||||
var output: [String]?
|
||||
var abilities: [String]?
|
||||
var contextWindow: Int
|
||||
var maxOutputTokens: Int
|
||||
var unsupportedEfforts: [String] = []
|
||||
var maxOutputTokens: Int?
|
||||
var supportedEfforts: [String]?
|
||||
var unsupportedEfforts: [String]?
|
||||
var effortWireValues: [String: String] = [:]
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"models": [
|
||||
{
|
||||
"provider": "openai",
|
||||
"id": "codex-auto-review",
|
||||
"name": "Codex Auto-review",
|
||||
"reasoning": true,
|
||||
"input": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"output": [
|
||||
"text"
|
||||
],
|
||||
"abilities": [
|
||||
"toolCall",
|
||||
"reasoning"
|
||||
],
|
||||
"contextWindow": 272000,
|
||||
"supportedEfforts": [
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh",
|
||||
"max"
|
||||
],
|
||||
"effortWireValues": {
|
||||
"xhigh": "xhigh",
|
||||
"max": "max"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -161,6 +161,55 @@ struct ModelCapabilityResolverTests {
|
||||
#expect(resolution.supported == [.off, .low, .medium, .high, .xHigh])
|
||||
}
|
||||
|
||||
@Test
|
||||
func bundledOverrideEnrichesCodexAutoReview() {
|
||||
let provider = ModelProvider(name: "OpenAI")
|
||||
let resolution = ModelCapabilityResolver.resolve(
|
||||
model: AIModel(id: "codex-auto-review"),
|
||||
provider: provider
|
||||
)
|
||||
|
||||
#expect(resolution.catalogSource == .init(provider: "openai", modelID: "codex-auto-review"))
|
||||
#expect(resolution.model.displayName == "Codex Auto-review")
|
||||
#expect(resolution.model.ownedBy == "openai")
|
||||
#expect(resolution.model.input == [.text, .image])
|
||||
#expect(resolution.model.output == [.text])
|
||||
#expect(resolution.model.abilities == [.toolCall, .reasoning])
|
||||
#expect(resolution.model.contextLength == 272_000)
|
||||
#expect(resolution.model.maxOutputTokens == nil)
|
||||
#expect(resolution.supported == [.low, .medium, .high, .xHigh, .max])
|
||||
#expect(resolution.wireValue(for: .xHigh) == "xhigh")
|
||||
#expect(resolution.wireValue(for: .max) == "max")
|
||||
#expect(resolution.clamp(.off) == .low)
|
||||
#expect(resolution.clamp(.minimal) == .low)
|
||||
}
|
||||
|
||||
@Test
|
||||
func reportedFieldsWinOverCodexAutoReviewOverride() {
|
||||
let provider = ModelProvider(name: "OpenAI")
|
||||
var model = AIModel(id: "codex-auto-review")
|
||||
model.kind = .other
|
||||
model.input = [.audio]
|
||||
model.output = [.image]
|
||||
model.abilities = []
|
||||
model.contextLength = 7
|
||||
model.maxOutputTokens = 8
|
||||
|
||||
let resolution = ModelCapabilityResolver.resolve(
|
||||
model: model,
|
||||
provider: provider,
|
||||
reported: [.kind, .input, .output, .abilities]
|
||||
)
|
||||
|
||||
#expect(resolution.model.kind == .other)
|
||||
#expect(resolution.model.input == [.audio])
|
||||
#expect(resolution.model.output == [.image])
|
||||
#expect(resolution.model.abilities.isEmpty)
|
||||
#expect(resolution.model.contextLength == 7)
|
||||
#expect(resolution.model.maxOutputTokens == 8)
|
||||
#expect(resolution.supported == [.off])
|
||||
}
|
||||
|
||||
@Test
|
||||
func explicitlyReportedFieldsWinOverPiCatalog() {
|
||||
let provider = ModelProvider(name: "OpenAI")
|
||||
|
||||
Reference in new issue
Block a user