|
1 | 1 | using System.Text; |
| 2 | +using System.Linq; |
2 | 3 | using System.Text.RegularExpressions; |
3 | 4 | using Microsoft.Extensions.Logging; |
4 | 5 | using Microsoft.Extensions.Options; |
|
38 | 39 | var stationOptions = Options.Create(new AutoClipperOptions { StationConfigPath = stationConfigPath }); |
39 | 40 | var stationConfiguration = new StationConfigurationService(stationOptions, loggerFactory.CreateLogger<StationConfigurationService>()); |
40 | 41 | var stationProfile = stationConfiguration.GetProfile(stationCode); |
41 | | - |
42 | 42 | var language = args.Length > 1 |
43 | 43 | ? args[1] |
44 | 44 | : Environment.GetEnvironmentVariable("AUTOCLIP_HARNESS_LANGUAGE") |
45 | 45 | ?? (!string.IsNullOrWhiteSpace(stationProfile.Transcription.Language) ? stationProfile.Transcription.Language : "en-US"); |
46 | 46 | var sampleRate = int.TryParse(Environment.GetEnvironmentVariable("AUTOCLIP_HARNESS_SAMPLE_RATE"), out var sr) |
47 | 47 | ? sr |
48 | 48 | : (stationProfile.Transcription.SampleRate > 0 ? stationProfile.Transcription.SampleRate : 16000); |
49 | | - |
50 | 49 | var audioNormalizer = new AudioNormalizer(loggerFactory.CreateLogger<AudioNormalizer>()); |
51 | 50 | var workingFile = await audioNormalizer.NormalizeAsync(input, sampleRate); |
| 51 | +var llmBaseUrl = RequireEnv("AUTOCLIP_HARNESS_LLM_URL").Trim(); |
| 52 | +var llmDeployment = Environment.GetEnvironmentVariable("AUTOCLIP_HARNESS_LLM_DEPLOYMENT"); |
| 53 | +var llmVersion = Environment.GetEnvironmentVariable("AUTOCLIP_HARNESS_LLM_VERSION") ?? "2024-07-18"; |
| 54 | +var llmModel = Environment.GetEnvironmentVariable("AUTOCLIP_HARNESS_LLM_MODEL"); |
| 55 | +var llmEndpoint = BuildLlmEndpointUri(llmBaseUrl, llmDeployment, llmVersion); |
| 56 | +var defaultModel = string.IsNullOrWhiteSpace(llmModel) |
| 57 | + ? (!string.IsNullOrWhiteSpace(llmDeployment) ? llmDeployment : "gpt-4o-mini") |
| 58 | + : llmModel; |
| 59 | + |
| 60 | + |
52 | 61 |
|
53 | 62 | var options = Options.Create(new AutoClipperOptions |
54 | 63 | { |
|
60 | 69 | AzureSpeechBatchTimeoutMinutes = int.TryParse(Environment.GetEnvironmentVariable("AUTOCLIP_HARNESS_BATCH_TIMEOUT_MINUTES"), out var batchTimeoutMinutes) ? batchTimeoutMinutes : 45, |
61 | 70 | AzureSpeechStorageConnectionString = RequireEnv("AUTOCLIP_HARNESS_STORAGE_CONNECTION_STRING"), |
62 | 71 | AzureSpeechStorageContainer = RequireEnv("AUTOCLIP_HARNESS_STORAGE_CONTAINER"), |
63 | | - AzureSpeechStorageSasExpiryMinutes = int.TryParse(Environment.GetEnvironmentVariable("AUTOCLIP_HARNESS_STORAGE_SAS_MINUTES"), out var sasMinutes) ? sasMinutes : 180, |
64 | | - LlmApiUrl = new Uri(RequireEnv("AUTOCLIP_HARNESS_LLM_URL")), |
| 72 | + AzureSpeechStorageSasExpiryMinutes = int.TryParse(Environment.GetEnvironmentVariable("AUTOCLIP_HARNESS_STORAGE_SAS_MINUTES"), out var sasMinutes) ? sasMinutes : 180, LlmApiUrl = llmEndpoint, |
| 73 | + |
65 | 74 | LlmApiKey = RequireEnv("AUTOCLIP_HARNESS_LLM_KEY"), |
| 75 | + |
| 76 | + LlmDefaultModel = defaultModel, |
66 | 77 | LlmPrompt = Environment.GetEnvironmentVariable("AUTOCLIP_HARNESS_PROMPT") |
67 | 78 | ?? (string.IsNullOrWhiteSpace(stationProfile.Text.LlmPrompt) ? string.Empty : stationProfile.Text.LlmPrompt), |
68 | 79 | MaxStoriesFromClip = int.TryParse(Environment.GetEnvironmentVariable("AUTOCLIP_HARNESS_MAX_STORIES"), out var maxStories) ? maxStories : 5, |
69 | 80 | VolumePath = Path.GetDirectoryName(Path.GetFullPath(input)) ?? ".", |
70 | 81 | DefaultTranscriptLanguage = stationProfile.Transcription.Language ?? "en-US" |
71 | 82 | }); |
72 | | - |
73 | 83 | var speechLogger = loggerFactory.CreateLogger<AzureSpeechTranscriptionService>(); |
74 | 84 | var llmLogger = loggerFactory.CreateLogger<ClipSegmentationService>(); |
75 | 85 | var speechService = new AzureSpeechTranscriptionService(new HttpClient(), options, speechLogger); |
76 | 86 | var llmService = new ClipSegmentationService(new HttpClient(), options, llmLogger); |
77 | | - |
78 | 87 | var transcriptionRequest = new SpeechTranscriptionRequest |
79 | 88 | { |
80 | 89 | Language = language, |
|
86 | 95 | Console.WriteLine($"[HARNESS] Transcribing {workingFile} ..."); |
87 | 96 | var segments = await speechService.TranscribeAsync(workingFile, transcriptionRequest, CancellationToken.None); |
88 | 97 | Console.WriteLine($"[HARNESS] Received {segments.Count} transcript segments"); |
89 | | - |
90 | 98 | var fullTranscriptBody = BuildTranscriptDocument(segments); |
91 | 99 | var fullTranscriptPath = Path.Combine(outputDir, "transcript_full.txt"); |
92 | 100 | await File.WriteAllTextAsync(fullTranscriptPath, fullTranscriptBody ?? string.Empty); |
93 | 101 | Console.WriteLine($"[HARNESS] Full transcript -> {fullTranscriptPath}"); |
94 | | - |
95 | 102 | var segmentationSettings = BuildSegmentationSettings(stationProfile); |
96 | 103 | Console.WriteLine("[HARNESS] Asking LLM for clip definitions ..."); |
97 | 104 | var promptDebugPath = Path.Combine(outputDir, "llm_prompt_debug.txt"); |
|
101 | 108 | .OrderBy(c => c.Start) |
102 | 109 | .ToArray(); |
103 | 110 | Console.WriteLine($"[HARNESS] LLM returned {clipDefinitions.Length} clip candidates"); |
104 | | - |
105 | 111 | var index = 1; |
106 | 112 | foreach (var definition in clipDefinitions) |
107 | 113 | { |
@@ -292,6 +298,19 @@ static async Task<string> CreateClipFileAsync(string srcFile, string outputDir, |
292 | 298 |
|
293 | 299 |
|
294 | 300 |
|
| 301 | + |
| 302 | +static Uri BuildLlmEndpointUri(string baseUrl, string? deployment, string apiVersion) |
| 303 | +{ |
| 304 | + if (string.IsNullOrWhiteSpace(baseUrl)) |
| 305 | + throw new InvalidOperationException("AUTOCLIP_HARNESS_LLM_URL must be set."); |
| 306 | + if (baseUrl.Contains("/chat/completions", StringComparison.OrdinalIgnoreCase)) |
| 307 | + return new Uri(baseUrl); |
| 308 | + if (string.IsNullOrWhiteSpace(deployment)) |
| 309 | + throw new InvalidOperationException("AUTOCLIP_HARNESS_LLM_DEPLOYMENT must be set when using a base LLM URL."); |
| 310 | + var version = string.IsNullOrWhiteSpace(apiVersion) ? "2024-07-18" : apiVersion; |
| 311 | + return new Uri($"{baseUrl.TrimEnd('/')}/openai/deployments/{deployment}/chat/completions?api-version={version}"); |
| 312 | +} |
| 313 | + |
295 | 314 | static ClipSegmentationSettings BuildSegmentationSettings(StationProfile profile) |
296 | 315 | { |
297 | 316 | return new ClipSegmentationSettings |
@@ -373,3 +392,4 @@ static IReadOnlyList<TimestampedTranscript> ExtractTranscriptRange(IReadOnlyList |
373 | 392 |
|
374 | 393 |
|
375 | 394 |
|
| 395 | + |
0 commit comments