|
| 1 | +import type { |
| 2 | + EngineIntentProfile, |
| 3 | + EngineRecommendation, |
| 4 | + EngineRecommendSuccess, |
| 5 | + RecommendedModel, |
| 6 | +} from "bailian-cli-core"; |
| 7 | + |
| 8 | +/** |
| 9 | + * recommend-adapter.ts — maps engine results onto the advisor-legacy JSON |
| 10 | + * contract so downstream consumers of `bl advisor recommend --output json` |
| 11 | + * see a stable shape: |
| 12 | + * |
| 13 | + * - single → { type: "single", recommendations: RecommendedModel[] } |
| 14 | + * - pipeline (multi-step) → { type: "pipeline", summary, steps: PipelineStep[] } |
| 15 | + * - top-level { intent, result, candidates } preserved |
| 16 | + * |
| 17 | + * Engine-only signals (confidence/slot/routingReason/degradations/specs) never |
| 18 | + * enter stdout JSON; they surface on stderr instead. |
| 19 | + */ |
| 20 | + |
| 21 | +/** Adapt an engine Recommendation onto the advisor-legacy RecommendedModel shape. */ |
| 22 | +function toRecommendedModel(rec: EngineRecommendation): RecommendedModel { |
| 23 | + return { |
| 24 | + model: rec.model, |
| 25 | + name: rec.name, |
| 26 | + reason: rec.reason, |
| 27 | + highlights: rec.highlights, |
| 28 | + contextWindow: rec.specs?.contextWindow, |
| 29 | + maxOutputTokens: rec.specs?.maxOutputTokens, |
| 30 | + docUrl: rec.docUrl, |
| 31 | + }; |
| 32 | +} |
| 33 | + |
| 34 | +/** Project the engine IntentProfile onto the advisor-legacy intent JSON shape. */ |
| 35 | +function toLegacyIntent(intent: EngineIntentProfile): Record<string, unknown> { |
| 36 | + return { |
| 37 | + taskSummary: intent.taskSummary, |
| 38 | + scenarioHints: intent.scenarioHints, |
| 39 | + complexity: intent.complexity, |
| 40 | + inputModality: intent.inputModality, |
| 41 | + outputModality: intent.outputModality, |
| 42 | + requiredCapabilities: intent.requiredCapabilities, |
| 43 | + budget: intent.budget, |
| 44 | + qualityPreference: intent.qualityPreference, |
| 45 | + modelPreference: |
| 46 | + intent.modelPreference?.mode !== "unconstrained" ? intent.modelPreference : undefined, |
| 47 | + segments: intent.segments, |
| 48 | + }; |
| 49 | +} |
| 50 | + |
| 51 | +export interface AdaptedCommandOutput { |
| 52 | + result: { type: "single" | "pipeline"; [key: string]: unknown }; |
| 53 | + intent: Record<string, unknown>; |
| 54 | + candidates: number; |
| 55 | +} |
| 56 | + |
| 57 | +/** Map an engine success result onto the legacy advisor output payload. */ |
| 58 | +export function adaptCommandOutput(result: EngineRecommendSuccess): AdaptedCommandOutput { |
| 59 | + let adapted: { type: "single" | "pipeline"; [key: string]: unknown }; |
| 60 | + if (result.steps?.length) { |
| 61 | + adapted = { |
| 62 | + type: "pipeline", |
| 63 | + summary: result.steps.map((step) => step.step).join(" → "), |
| 64 | + steps: result.steps.map((step) => ({ |
| 65 | + step: step.step, |
| 66 | + recommendations: step.recommendations.map(toRecommendedModel), |
| 67 | + })), |
| 68 | + ...(result.warnings?.length ? { warnings: result.warnings } : {}), |
| 69 | + }; |
| 70 | + } else { |
| 71 | + adapted = { |
| 72 | + type: "single", |
| 73 | + recommendations: result.recommendations.map(toRecommendedModel), |
| 74 | + }; |
| 75 | + } |
| 76 | + return { |
| 77 | + result: adapted, |
| 78 | + intent: toLegacyIntent(result.intent), |
| 79 | + candidates: result.candidatePool, |
| 80 | + }; |
| 81 | +} |
0 commit comments