@@ -108,6 +108,19 @@ public object Any(GetModelImage request)
108
108
return GetModelImage ( request . Model ) ;
109
109
}
110
110
111
+ public async Task < object > Post ( OllamaGeneration request )
112
+ {
113
+ var generateRequest = new QueueOllamaGeneration
114
+ {
115
+ Request = request ,
116
+ RefId = request . RefId ,
117
+ Tag = request . Tag ,
118
+ Provider = request . Provider
119
+ } ;
120
+
121
+ return await generateRequest . ProcessSync ( jobs , this ) ;
122
+ }
123
+
111
124
public async Task < object > Post ( OpenAiChatCompletion request )
112
125
{
113
126
var chatRequest = new QueueOpenAiChatCompletion
@@ -121,6 +134,70 @@ public async Task<object> Post(OpenAiChatCompletion request)
121
134
return await chatRequest . ProcessSync ( jobs , this ) ;
122
135
}
123
136
137
+ public QueueOllamaGenerationResponse Any ( QueueOllamaGeneration request )
138
+ {
139
+ if ( request . Request == null )
140
+ throw new ArgumentNullException ( nameof ( request . Request ) ) ;
141
+
142
+ if ( request . Request . Prompt . IsNullOrEmpty ( ) )
143
+ throw new ArgumentNullException ( nameof ( request . Request . Prompt ) ) ;
144
+
145
+ var qualifiedModel = appData . GetQualifiedModel ( request . Request . Model ) ;
146
+ if ( qualifiedModel == null )
147
+ throw HttpError . NotFound ( $ "Model { request . Request . Model } not found") ;
148
+
149
+ var queueCounts = jobs . GetWorkerQueueCounts ( ) ;
150
+ var providerQueueCount = int . MaxValue ;
151
+ AiProvider ? useProvider = null ;
152
+ var candidates = appData . AiProviders
153
+ . Where ( x => x is { Enabled : true , AiType . Provider : AiProviderType . OllamaAiProvider }
154
+ && x . Models . Any ( m => m . Model == qualifiedModel ) ) . ToList ( ) ;
155
+ foreach ( var candidate in candidates )
156
+ {
157
+ if ( candidate . OfflineDate != null )
158
+ continue ;
159
+ var pendingJobs = queueCounts . GetValueOrDefault ( candidate . Name , 0 ) ;
160
+ if ( useProvider == null )
161
+ {
162
+ useProvider = candidate ;
163
+ providerQueueCount = pendingJobs ;
164
+ continue ;
165
+ }
166
+ if ( pendingJobs < providerQueueCount || ( pendingJobs == providerQueueCount && candidate . Priority > useProvider . Priority ) )
167
+ {
168
+ useProvider = candidate ;
169
+ providerQueueCount = pendingJobs ;
170
+ }
171
+ }
172
+
173
+ useProvider ??= candidates . FirstOrDefault ( x => x . Name == qualifiedModel ) ; // Allow selecting offline models
174
+ if ( useProvider == null )
175
+ throw new NotSupportedException ( "No active AI Providers support this model" ) ;
176
+
177
+ var jobRef = jobs . EnqueueCommand < CreateOllamaGenerationCommand > ( request , new ( )
178
+ {
179
+ RefId = request . RefId ,
180
+ ReplyTo = request . ReplyTo ,
181
+ Tag = request . Tag ,
182
+ Args = request . Provider == null ? null : new ( ) {
183
+ [ nameof ( request . Provider ) ] = request . Provider
184
+ } ,
185
+ Worker = useProvider . Name ,
186
+ } ) ;
187
+
188
+ var jobStatusUrl = AppConfig . Instance . ApplicationBaseUrl
189
+ . CombineWith ( $ "/api/{ nameof ( GetOllamaGenerationStatus ) } ?RefId=" + jobRef . RefId ) ;
190
+
191
+ var response = new QueueOllamaGenerationResponse
192
+ {
193
+ Id = jobRef . Id ,
194
+ RefId = jobRef . RefId ,
195
+ StatusUrl = jobStatusUrl
196
+ } ;
197
+
198
+ return response ;
199
+ }
200
+
124
201
public QueueOpenAiChatResponse Any ( QueueOpenAiChatCompletion request )
125
202
{
126
203
if ( request . Request == null )
@@ -292,6 +369,40 @@ private async Task<JobResult> WaitForJobCompletion(long jobId)
292
369
return HttpError . NotFound ( "Job not found" ) ;
293
370
}
294
371
372
+ public async Task < object > Get ( GetOllamaGenerationStatus request )
373
+ {
374
+ var summary = GetJobSummary ( ( int ) request . JobId , request . RefId ) ;
375
+ if ( summary == null )
376
+ return HttpError . NotFound ( "JobSummary not found" ) ;
377
+
378
+ var response = GetOpenAiChat ( summary ) ;
379
+ if ( response == null )
380
+ return HttpError . NotFound ( "Job not found" ) ;
381
+
382
+ var job = response . Result ;
383
+
384
+ var generateResponse = response . Result ? . ResponseBody . FromJson < OllamaGenerateResponse > ( ) ;
385
+ if ( generateResponse == null )
386
+ {
387
+ return new GetOllamaGenerationStatusResponse
388
+ {
389
+ JobId = request . JobId ,
390
+ RefId = request . RefId ,
391
+ JobState = job . State ,
392
+ Status = job . State . ToString ( ) ,
393
+ } ;
394
+ }
395
+
396
+ return new GetOllamaGenerationStatusResponse
397
+ {
398
+ JobId = request . JobId ,
399
+ RefId = request . RefId ,
400
+ JobState = job . State ,
401
+ Status = job . State . ToString ( ) ,
402
+ Result = generateResponse ,
403
+ } ;
404
+ }
405
+
295
406
public async Task < object > Get ( GetOpenAiChatStatus request )
296
407
{
297
408
var summary = GetJobSummary ( ( int ) request . JobId , request . RefId ) ;
@@ -428,6 +539,81 @@ public object Any(DeleteAiProvider request)
428
539
429
540
public static class OpenAiChatServiceExtensions
430
541
{
542
+ public static async Task < OllamaGenerateResponse > ProcessSync ( this QueueOllamaGeneration generateRequest ,
543
+ IBackgroundJobs jobs , OpenAiChatServices chatService )
544
+ {
545
+ QueueOllamaGenerationResponse ? generateResponse = null ;
546
+ try
547
+ {
548
+ var response = chatService . Any ( generateRequest ) ;
549
+ generateResponse = response ;
550
+ }
551
+ catch ( Exception e )
552
+ {
553
+ Console . WriteLine ( e ) ;
554
+ throw ;
555
+ }
556
+
557
+ if ( generateResponse == null )
558
+ throw new Exception ( "Failed to start chat request" ) ;
559
+
560
+ var job = jobs . GetJob ( generateResponse . Id ) ;
561
+ // For all requests, wait for the job to be created
562
+ while ( job == null )
563
+ {
564
+ await Task . Delay ( 1000 ) ;
565
+ job = jobs . GetJob ( generateResponse . Id ) ;
566
+ }
567
+
568
+ // We know at this point, we definitely have a job
569
+ JobResult queuedJob = job ;
570
+
571
+ var completedResponse = new OllamaGenerateResponse ( ) ;
572
+
573
+ // Handle failed jobs
574
+ if ( job . Failed != null )
575
+ {
576
+ throw new Exception ( job . Failed . Error ! . Message ) ;
577
+ }
578
+
579
+ // Wait for the job to complete max 2 minutes
580
+ var timeout = DateTime . UtcNow . AddMinutes ( 2 ) ;
581
+ while ( queuedJob ? . Job ? . State is not ( BackgroundJobState . Completed or BackgroundJobState . Cancelled
582
+ or BackgroundJobState . Failed ) && DateTime . UtcNow < timeout )
583
+ {
584
+ await Task . Delay ( 1000 ) ;
585
+ queuedJob = jobs . GetJob ( generateResponse . Id ) ;
586
+ }
587
+
588
+ // Check if the job is still not completed
589
+ if ( queuedJob ? . Job ? . State is not ( BackgroundJobState . Completed or BackgroundJobState . Cancelled
590
+ or BackgroundJobState . Failed ) )
591
+ {
592
+ throw new Exception ( "Job did not complete within the specified timeout." ) ;
593
+ }
594
+
595
+ // Process successful job results
596
+ var jobResponseBody = queuedJob . Completed ? . ResponseBody ;
597
+ var jobRes = jobResponseBody . FromJson < OllamaGenerateResponse > ( ) ;
598
+ if ( jobRes != null )
599
+ {
600
+ completedResponse . Model = jobRes . Model ;
601
+ completedResponse . CreatedAt = jobRes . CreatedAt ;
602
+ completedResponse . Response = jobRes . Response ;
603
+ completedResponse . Done = jobRes . Done ;
604
+ completedResponse . Context = jobRes . Context ;
605
+ completedResponse . DoneReason = jobRes . DoneReason ;
606
+ completedResponse . TotalDuration = jobRes . TotalDuration ;
607
+ completedResponse . LoadDuration = jobRes . LoadDuration ;
608
+ completedResponse . PromptEvalCount = jobRes . PromptEvalCount ;
609
+ completedResponse . EvalCount = jobRes . EvalCount ;
610
+ completedResponse . PromptTokens = jobRes . PromptTokens ;
611
+ completedResponse . ResponseStatus = jobRes . ResponseStatus ;
612
+ }
613
+
614
+ return completedResponse ;
615
+ }
616
+
431
617
public static async Task < OpenAiChatResponse > ProcessSync ( this QueueOpenAiChatCompletion chatRequest ,
432
618
IBackgroundJobs jobs , OpenAiChatServices chatService )
433
619
{
@@ -443,7 +629,7 @@ public static async Task<OpenAiChatResponse> ProcessSync(this QueueOpenAiChatCom
443
629
throw ;
444
630
}
445
631
446
- if ( chatResponse == null )
632
+ if ( chatResponse == null )
447
633
throw new Exception ( "Failed to start chat request" ) ;
448
634
449
635
var job = jobs . GetJob ( chatResponse . Id ) ;
0 commit comments