1
1
// src/llms/ollama.ts
2
2
3
3
import { env } from 'node:process'
4
- import { spawn } from 'node:child_process'
5
- import { OLLAMA_MODELS } from '../utils/llm-models'
4
+ import { OLLAMA_MODELS } from '../utils/llm-globals'
6
5
import { l , err , logAPIResults } from '../utils/logging'
7
- import type { LLMFunction , OllamaModelType , OllamaResponse , OllamaTagsResponse } from '../types/llms'
6
+ import { checkServerAndModel } from '../utils/validate-option'
7
+ import type { LLMFunction , OllamaModelType , OllamaResponse } from '../types/llms'
8
8
9
9
/**
10
10
* callOllama()
@@ -38,94 +38,7 @@ export const callOllama: LLMFunction = async (
38
38
39
39
const combinedPrompt = `${ prompt } \n${ transcript } `
40
40
41
- async function checkServer ( ) : Promise < boolean > {
42
- try {
43
- const serverResponse = await fetch ( `http://${ ollamaHost } :${ ollamaPort } ` )
44
- return serverResponse . ok
45
- } catch ( error ) {
46
- return false
47
- }
48
- }
49
-
50
- if ( await checkServer ( ) ) {
51
- l . wait ( '\n Ollama server is already running...' )
52
- } else {
53
- if ( ollamaHost === 'ollama' ) {
54
- throw new Error ( 'Ollama server is not running. Please ensure the Ollama server is running and accessible.' )
55
- } else {
56
- l . wait ( '\n Ollama server is not running. Attempting to start...' )
57
- const ollamaProcess = spawn ( 'ollama' , [ 'serve' ] , {
58
- detached : true ,
59
- stdio : 'ignore' ,
60
- } )
61
- ollamaProcess . unref ( )
62
-
63
- let attempts = 0
64
- while ( attempts < 30 ) {
65
- if ( await checkServer ( ) ) {
66
- l . wait ( ' - Ollama server is now ready.' )
67
- break
68
- }
69
- await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) )
70
- attempts ++
71
- }
72
- if ( attempts === 30 ) {
73
- throw new Error ( 'Ollama server failed to become ready in time.' )
74
- }
75
- }
76
- }
77
-
78
- l . wait ( `\n Checking if model is available: ${ ollamaModelName } ` )
79
- try {
80
- const tagsResponse = await fetch ( `http://${ ollamaHost } :${ ollamaPort } /api/tags` )
81
- if ( ! tagsResponse . ok ) {
82
- throw new Error ( `HTTP error! status: ${ tagsResponse . status } ` )
83
- }
84
- const tagsData = ( await tagsResponse . json ( ) ) as OllamaTagsResponse
85
- const isModelAvailable = tagsData . models . some ( ( m ) => m . name === ollamaModelName )
86
-
87
- if ( ! isModelAvailable ) {
88
- l . wait ( `\n Model ${ ollamaModelName } is not available, pulling...` )
89
- const pullResponse = await fetch ( `http://${ ollamaHost } :${ ollamaPort } /api/pull` , {
90
- method : 'POST' ,
91
- headers : { 'Content-Type' : 'application/json' } ,
92
- body : JSON . stringify ( { name : ollamaModelName } ) ,
93
- } )
94
- if ( ! pullResponse . ok ) {
95
- throw new Error ( `Failed to initiate pull for model ${ ollamaModelName } ` )
96
- }
97
- if ( ! pullResponse . body ) {
98
- throw new Error ( 'Response body is null' )
99
- }
100
-
101
- const reader = pullResponse . body . getReader ( )
102
- const decoder = new TextDecoder ( )
103
- while ( true ) {
104
- const { done, value } = await reader . read ( )
105
- if ( done ) break
106
-
107
- const chunk = decoder . decode ( value )
108
- const lines = chunk . split ( '\n' )
109
- for ( const line of lines ) {
110
- if ( line . trim ( ) === '' ) continue
111
- try {
112
- const parsedLine = JSON . parse ( line )
113
- if ( parsedLine . status === 'success' ) {
114
- l . wait ( ` - Model ${ ollamaModelName } pulled successfully.\n` )
115
- break
116
- }
117
- } catch ( parseError ) {
118
- err ( `Error parsing JSON while pulling model: ${ parseError } ` )
119
- }
120
- }
121
- }
122
- } else {
123
- l . wait ( `\n Model ${ ollamaModelName } is already available.\n` )
124
- }
125
- } catch ( error ) {
126
- err ( `Error checking/pulling model: ${ ( error as Error ) . message } ` )
127
- throw error
128
- }
41
+ await checkServerAndModel ( ollamaHost , ollamaPort , ollamaModelName )
129
42
130
43
l . wait ( ` - Sending chat request to http://${ ollamaHost } :${ ollamaPort } using model '${ ollamaModelName } '` )
131
44
@@ -135,68 +48,29 @@ export const callOllama: LLMFunction = async (
135
48
body : JSON . stringify ( {
136
49
model : ollamaModelName ,
137
50
messages : [ { role : 'user' , content : combinedPrompt } ] ,
138
- stream : true ,
51
+ stream : false ,
139
52
} ) ,
140
53
} )
141
54
142
55
if ( ! response . ok ) {
143
56
throw new Error ( `HTTP error! status: ${ response . status } ` )
144
57
}
145
- if ( ! response . body ) {
146
- throw new Error ( 'Response body is null' )
147
- }
148
-
149
- l . wait ( '\n Successfully connected to Ollama /api/chat streaming endpoint.' )
150
- const reader = response . body . getReader ( )
151
- const decoder = new TextDecoder ( )
152
- let fullContent = ''
153
- let isFirstChunk = true
154
- let totalPromptTokens = 0
155
- let totalCompletionTokens = 0
156
-
157
- while ( true ) {
158
- const { done, value } = await reader . read ( )
159
- if ( done ) break
160
58
161
- const chunk = decoder . decode ( value )
162
- const lines = chunk . split ( '\n' )
59
+ const data = await response . json ( ) as OllamaResponse
60
+ const fullContent = data ?. message ?. content || ''
163
61
164
- for ( const line of lines ) {
165
- if ( line . trim ( ) === '' ) continue
62
+ const totalPromptTokens = data . prompt_eval_count ?? 0
63
+ const totalCompletionTokens = data . eval_count ?? 0
166
64
167
- try {
168
- const parsedResponse = JSON . parse ( line ) as OllamaResponse
169
- if ( parsedResponse . message ?. content ) {
170
- if ( isFirstChunk ) {
171
- l . wait ( ` - Streaming response from Ollama (first chunk received)` )
172
- isFirstChunk = false
173
- }
174
- fullContent += parsedResponse . message . content
175
- }
176
-
177
- if ( parsedResponse . prompt_eval_count ) {
178
- totalPromptTokens = parsedResponse . prompt_eval_count
179
- }
180
- if ( parsedResponse . eval_count ) {
181
- totalCompletionTokens = parsedResponse . eval_count
182
- }
183
-
184
- if ( parsedResponse . done ) {
185
- logAPIResults ( {
186
- modelName : modelKey ,
187
- stopReason : 'stop' ,
188
- tokenUsage : {
189
- input : totalPromptTokens || undefined ,
190
- output : totalCompletionTokens || undefined ,
191
- total : totalPromptTokens + totalCompletionTokens || undefined ,
192
- } ,
193
- } )
194
- }
195
- } catch ( parseError ) {
196
- err ( `Error parsing JSON from Ollama response: ${ parseError } ` )
197
- }
198
- }
199
- }
65
+ logAPIResults ( {
66
+ modelName : modelKey ,
67
+ stopReason : 'stop' ,
68
+ tokenUsage : {
69
+ input : totalPromptTokens || undefined ,
70
+ output : totalCompletionTokens || undefined ,
71
+ total : totalPromptTokens + totalCompletionTokens || undefined ,
72
+ } ,
73
+ } )
200
74
201
75
return fullContent
202
76
} catch ( error ) {
0 commit comments