client.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. import os
  2. import json
  3. import requests
  4. import os
  5. import hashlib
  6. import json
  7. from pathlib import Path
  8. BASE_URL = os.environ.get('OLLAMA_HOST', 'http://localhost:11434')
  9. # Generate a response for a given prompt with a provided model. This is a streaming endpoint, so will be a series of responses.
  10. # The final response object will include statistics and additional data from the request. Use the callback function to override
  11. # the default handler.
  12. def generate(model_name, prompt, system=None, template=None, format="", context=None, options=None, callback=None):
  13. try:
  14. url = f"{BASE_URL}/api/generate"
  15. payload = {
  16. "model": model_name,
  17. "prompt": prompt,
  18. "system": system,
  19. "template": template,
  20. "context": context,
  21. "options": options,
  22. "format": format,
  23. }
  24. # Remove keys with None values
  25. payload = {k: v for k, v in payload.items() if v is not None}
  26. with requests.post(url, json=payload, stream=True) as response:
  27. response.raise_for_status()
  28. # Creating a variable to hold the context history of the final chunk
  29. final_context = None
  30. # Variable to hold concatenated response strings if no callback is provided
  31. full_response = ""
  32. # Iterating over the response line by line and displaying the details
  33. for line in response.iter_lines():
  34. if line:
  35. # Parsing each line (JSON chunk) and extracting the details
  36. chunk = json.loads(line)
  37. # If a callback function is provided, call it with the chunk
  38. if callback:
  39. callback(chunk)
  40. else:
  41. # If this is not the last chunk, add the "response" field value to full_response and print it
  42. if not chunk.get("done"):
  43. response_piece = chunk.get("response", "")
  44. full_response += response_piece
  45. print(response_piece, end="", flush=True)
  46. # Check if it's the last chunk (done is true)
  47. if chunk.get("done"):
  48. final_context = chunk.get("context")
  49. # Return the full response and the final context
  50. return full_response, final_context
  51. except requests.exceptions.RequestException as e:
  52. print(f"An error occurred: {e}")
  53. return None, None
  54. # Create a blob file on the server if it doesn't exist.
  55. def create_blob(digest, file_path):
  56. url = f"{BASE_URL}/api/blobs/{digest}"
  57. # Check if the blob exists
  58. response = requests.head(url)
  59. if response.status_code != 404:
  60. return # Blob already exists, no need to upload
  61. response.raise_for_status()
  62. # Upload the blob
  63. with open(file_path, 'rb') as file_data:
  64. requests.post(url, data=file_data)
  65. # Create a model from a Modelfile. Use the callback function to override the default handler.
  66. def create(model_name, filename, callback=None):
  67. try:
  68. file_path = Path(filename).expanduser().resolve()
  69. processed_lines = []
  70. # Read and process the modelfile
  71. with open(file_path, 'r') as f:
  72. for line in f:
  73. # Skip empty or whitespace-only lines
  74. if not line.strip():
  75. continue
  76. command, args = line.split(maxsplit=1)
  77. if command.upper() in ["FROM", "ADAPTER"]:
  78. path = Path(args.strip()).expanduser()
  79. # Check if path is relative and resolve it
  80. if not path.is_absolute():
  81. path = (file_path.parent / path)
  82. # Skip if file does not exist for "model", this is handled by the server
  83. if not path.exists():
  84. processed_lines.append(line)
  85. continue
  86. # Calculate SHA-256 hash
  87. with open(path, 'rb') as bin_file:
  88. hash = hashlib.sha256()
  89. hash.update(bin_file.read())
  90. blob = f"sha256:{hash.hexdigest()}"
  91. # Add the file to the remote server
  92. create_blob(blob, path)
  93. # Replace path with digest in the line
  94. line = f"{command} @{blob}\n"
  95. processed_lines.append(line)
  96. # Combine processed lines back into a single string
  97. modelfile_content = '\n'.join(processed_lines)
  98. url = f"{BASE_URL}/api/create"
  99. payload = {"name": model_name, "modelfile": modelfile_content}
  100. # Making a POST request with the stream parameter set to True to handle streaming responses
  101. with requests.post(url, json=payload, stream=True) as response:
  102. response.raise_for_status()
  103. # Iterating over the response line by line and displaying the status
  104. for line in response.iter_lines():
  105. if line:
  106. chunk = json.loads(line)
  107. if callback:
  108. callback(chunk)
  109. else:
  110. print(f"Status: {chunk.get('status')}")
  111. except Exception as e:
  112. print(f"An error occurred: {e}")
  113. # Pull a model from a the model registry. Cancelled pulls are resumed from where they left off, and multiple
  114. # calls to will share the same download progress. Use the callback function to override the default handler.
  115. def pull(model_name, insecure=False, callback=None):
  116. try:
  117. url = f"{BASE_URL}/api/pull"
  118. payload = {
  119. "name": model_name,
  120. "insecure": insecure
  121. }
  122. # Making a POST request with the stream parameter set to True to handle streaming responses
  123. with requests.post(url, json=payload, stream=True) as response:
  124. response.raise_for_status()
  125. # Iterating over the response line by line and displaying the details
  126. for line in response.iter_lines():
  127. if line:
  128. # Parsing each line (JSON chunk) and extracting the details
  129. chunk = json.loads(line)
  130. # If a callback function is provided, call it with the chunk
  131. if callback:
  132. callback(chunk)
  133. else:
  134. # Print the status message directly to the console
  135. print(chunk.get('status', ''), end='', flush=True)
  136. # If there's layer data, you might also want to print that (adjust as necessary)
  137. if 'digest' in chunk:
  138. print(f" - Digest: {chunk['digest']}", end='', flush=True)
  139. print(f" - Total: {chunk['total']}", end='', flush=True)
  140. print(f" - Completed: {chunk['completed']}", end='\n', flush=True)
  141. else:
  142. print()
  143. except requests.exceptions.RequestException as e:
  144. print(f"An error occurred: {e}")
  145. # Push a model to the model registry. Use the callback function to override the default handler.
  146. def push(model_name, insecure=False, callback=None):
  147. try:
  148. url = f"{BASE_URL}/api/push"
  149. payload = {
  150. "name": model_name,
  151. "insecure": insecure
  152. }
  153. # Making a POST request with the stream parameter set to True to handle streaming responses
  154. with requests.post(url, json=payload, stream=True) as response:
  155. response.raise_for_status()
  156. # Iterating over the response line by line and displaying the details
  157. for line in response.iter_lines():
  158. if line:
  159. # Parsing each line (JSON chunk) and extracting the details
  160. chunk = json.loads(line)
  161. # If a callback function is provided, call it with the chunk
  162. if callback:
  163. callback(chunk)
  164. else:
  165. # Print the status message directly to the console
  166. print(chunk.get('status', ''), end='', flush=True)
  167. # If there's layer data, you might also want to print that (adjust as necessary)
  168. if 'digest' in chunk:
  169. print(f" - Digest: {chunk['digest']}", end='', flush=True)
  170. print(f" - Total: {chunk['total']}", end='', flush=True)
  171. print(f" - Completed: {chunk['completed']}", end='\n', flush=True)
  172. else:
  173. print()
  174. except requests.exceptions.RequestException as e:
  175. print(f"An error occurred: {e}")
  176. # List models that are available locally.
  177. def list():
  178. try:
  179. response = requests.get(f"{BASE_URL}/api/tags")
  180. response.raise_for_status()
  181. data = response.json()
  182. models = data.get('models', [])
  183. return models
  184. except requests.exceptions.RequestException as e:
  185. print(f"An error occurred: {e}")
  186. return None
  187. # Copy a model. Creates a model with another name from an existing model.
  188. def copy(source, destination):
  189. try:
  190. # Create the JSON payload
  191. payload = {
  192. "source": source,
  193. "destination": destination
  194. }
  195. response = requests.post(f"{BASE_URL}/api/copy", json=payload)
  196. response.raise_for_status()
  197. # If the request was successful, return a message indicating that the copy was successful
  198. return "Copy successful"
  199. except requests.exceptions.RequestException as e:
  200. print(f"An error occurred: {e}")
  201. return None
  202. # Delete a model and its data.
  203. def delete(model_name):
  204. try:
  205. url = f"{BASE_URL}/api/delete"
  206. payload = {"name": model_name}
  207. response = requests.delete(url, json=payload)
  208. response.raise_for_status()
  209. return "Delete successful"
  210. except requests.exceptions.RequestException as e:
  211. print(f"An error occurred: {e}")
  212. return None
  213. # Show info about a model.
  214. def show(model_name):
  215. try:
  216. url = f"{BASE_URL}/api/show"
  217. payload = {"name": model_name}
  218. response = requests.post(url, json=payload)
  219. response.raise_for_status()
  220. # Parse the JSON response and return it
  221. data = response.json()
  222. return data
  223. except requests.exceptions.RequestException as e:
  224. print(f"An error occurred: {e}")
  225. return None
  226. def heartbeat():
  227. try:
  228. url = f"{BASE_URL}/"
  229. response = requests.head(url)
  230. response.raise_for_status()
  231. return "Ollama is running"
  232. except requests.exceptions.RequestException as e:
  233. print(f"An error occurred: {e}")
  234. return "Ollama is not running"