File size: 5,679 Bytes
f778c12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import path from "node:path";
import type { Command } from "commander";
import { resolveAgentDir } from "../../agents/agent-scope.js";
import { inspectLocalAudioSelection } from "../../media-understanding/local-audio.js";
import { buildMediaUnderstandingRegistry } from "../../media-understanding/provider-registry.js";
import { transcribeAudioFile } from "../../media-understanding/runtime.js";
import { defaultRuntime } from "../../runtime.js";
import { getProviderEnvVars } from "../../secrets/provider-env-vars.js";
import { runCommandWithRuntime } from "../cli-utils.js";
import { getModelsCommandSecretTargetIds } from "../command-secret-targets.js";
import { prepareLocalCapabilityAccountSecrets } from "./local-account-secrets.js";
import { isMissingMediaUnderstandingProvider } from "./media-understanding-result.js";
import type { CapabilityEnvelope } from "./metadata.js";
import { emitJsonOrText, formatEnvelopeForText, providerSummaryText } from "./output.js";
import {
  providerHasGenericConfig,
  registerLocalProvidersCommand,
  requireProviderModelOverride,
  resolveCapabilityAgentOption,
  resolveCapabilityProviderAgentId,
  resolveLocalCapabilityRuntimeConfig,
} from "./shared.js";

async function runAudioTranscribe(params: {
  file: string;
  language?: string;
  model?: string;
  prompt?: string;
  agent?: string;
}) {
  const cfg = await resolveLocalCapabilityRuntimeConfig({
    commandName: "infer audio transcribe",
    targetIds: getModelsCommandSecretTargetIds(),
  });
  const agentId = resolveCapabilityProviderAgentId(cfg, params.agent, "infer audio transcribe");
  await prepareLocalCapabilityAccountSecrets({ cfg, agentId });
  const result = await transcribeAudioFile({
    agentDir: resolveAgentDir(cfg, agentId),
    activeModel: requireProviderModelOverride(params.model),
    filePath: path.resolve(params.file),
    cfg,
    agentId,
    language: params.language,
    prompt: params.prompt,
  });
  if (!result.text) {
    if (isMissingMediaUnderstandingProvider(result)) {
      throw new Error(
        "No audio transcription provider is configured or ready. Configure an audio-capable tools.media.models entry, or pass --model <provider/model> after configuring that provider's auth/API key.",
      );
    }
    throw new Error(`No transcript returned for audio: ${path.resolve(params.file)}`);
  }
  return {
    ok: true,
    capability: "audio.transcribe",
    transport: "local" as const,
    provider: result.provider,
    model: result.model,
    attempts: [],
    outputs: [{ path: path.resolve(params.file), text: result.text, kind: "audio.transcription" }],
  } satisfies CapabilityEnvelope;
}

export function registerAudioCapabilityCommands(capability: Command): void {
  const audio = capability
    .command("audio")
    .description("Audio transcription")
    .option("--agent <id>", "Agent whose model and auth state should be used");

  audio
    .command("transcribe")
    .description("Transcribe one audio file")
    .requiredOption("--file <path>", "Audio file")
    .option("--agent <id>", "Agent whose model and auth state should be used")
    .option("--language <code>", "Language hint")
    .option("--prompt <text>", "Prompt hint")
    .option("--model <provider/model>", "Model override")
    .option("--json", "Output JSON", false)
    .action(async (opts, command) => {
      await runCommandWithRuntime(defaultRuntime, async () => {
        const result = await runAudioTranscribe({
          file: String(opts.file),
          agent: resolveCapabilityAgentOption(command, opts.agent),
          language: opts.language as string | undefined,
          model: opts.model as string | undefined,
          prompt: opts.prompt as string | undefined,
        });
        emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText);
      });
    });

  registerLocalProvidersCommand(
    audio,
    "List audio transcription providers",
    async (cfg, agentId) => {
      const remoteProviders = [...buildMediaUnderstandingRegistry(undefined, cfg).values()]
        .filter((provider) => provider.capabilities?.includes("audio"))
        .map((provider) => ({
          available: true,
          configured: providerHasGenericConfig({
            cfg,
            providerId: provider.id,
            agentId,
            envVars: getProviderEnvVars(provider.id, {
              config: cfg,
              includeUntrustedWorkspacePlugins: false,
            }),
          }),
          selected: false,
          id: provider.id,
          capabilities: provider.capabilities,
          defaultModels: provider.defaultModels,
        }));
      const localSelection = await inspectLocalAudioSelection();
      const localProviders = localSelection.candidates
        .filter((candidate) => candidate.available)
        .map((candidate) =>
          Object.assign(
            {
              available: candidate.available,
              configured: candidate.ready,
              selected: false,
              localFallbackSelected: candidate.selected,
              id: `local/${candidate.id}`,
              transport: "local-cli",
              command: candidate.command,
              observedBackend: candidate.observedBackend ?? "unknown",
              evidence: candidate.evidence,
            },
            candidate.capableBackend ? { capableBackend: candidate.capableBackend } : {},
            candidate.requestedBackend ? { requestedBackend: candidate.requestedBackend } : {},
            candidate.reason ? { reason: candidate.reason } : {},
          ),
        );
      return [...remoteProviders, ...localProviders];
    },
    providerSummaryText,
  );
}