Bruce MacDonald hai 1 ano
pai
achega
fd24bcabf8
Modificáronse 4 ficheiros con 85 adicións e 68 borrados
  1. 1 1
      examples/README.md
  2. 25 8
      src/index.ts
  3. 53 55
      src/node.js
  4. 6 4
      src/utils.ts

+ 1 - 1
examples/README.md

@@ -7,4 +7,4 @@ To run the examples run:
 
 ```sh
 npx tsx <folder-name>/<file-name>.ts
-```
+```

+ 25 - 8
src/index.ts

@@ -101,7 +101,7 @@ export class Ollama {
       return result
     }
     if (utils.isNode()) {
-      const { readImage } = await import('../src/node.js');
+      const { readImage } = await import('../src/node.js')
       try {
         // if this succeeds the image exists locally at this filepath and has been read
         return await readImage(image)
@@ -122,7 +122,17 @@ export class Ollama {
     request: GenerateRequest,
   ): Promise<GenerateResponse | AsyncGenerator<GenerateResponse>> {
     if (request.images) {
-      request.images = await Promise.all(request.images.map(this.encodeImage.bind(this)))
+      const encodedImages: (Uint8Array | string)[] = await Promise.all(
+        request.images.map(this.encodeImage.bind(this)),
+      )
+      // Fix type checks by only allowing one type in the array.
+      if (encodedImages.length > 0) {
+        if (typeof encodedImages[0] === 'string') {
+          request.images = encodedImages as string[]
+        } else {
+          request.images = encodedImages as Uint8Array[]
+        }
+      }
     }
     return this.processStreamableRequest<GenerateResponse>('generate', request)
   }
@@ -134,9 +144,17 @@ export class Ollama {
     if (request.messages) {
       for (const message of request.messages) {
         if (message.images) {
-          message.images = await Promise.all(
+          const encodedImages: (Uint8Array | string)[] = await Promise.all(
             message.images.map(this.encodeImage.bind(this)),
           )
+          // Fix type checks by only allowing one type in the array.
+          if (encodedImages.length > 0) {
+            if (typeof encodedImages[0] === 'string') {
+              message.images = encodedImages as string[]
+            } else {
+              message.images = encodedImages as Uint8Array[]
+            }
+          }
         }
       }
     }
@@ -177,16 +195,15 @@ export class Ollama {
   async create(
     request: CreateRequest,
   ): Promise<ProgressResponse | AsyncGenerator<ProgressResponse>> {
-
     if (utils.isNode()) {
-      const { readModelfile } = await import('../src/node.js');
-      let modelfileContent = await readModelfile(this, request);
+      const { readModelfile } = await import('../src/node.js')
+      const modelfileContent = await readModelfile(this, request)
       request.modelfile = modelfileContent
     }
 
-    if (request.modelfile == "") {
+    if (request.modelfile == '') {
       // request.path will resolve to a modelfile in node environments, otherwise is it required
-      throw new Error("modelfile is requrired")
+      throw new Error('modelfile is requrired')
     }
 
     return this.processStreamableRequest<ProgressResponse>('create', {

+ 53 - 55
src/node.js

@@ -1,113 +1,111 @@
-const fs = require('fs');
-const path = require('path');
-const utils = require('./utils');
-const { createHash } = require('crypto');
-const { homedir } = require('os');
+const fs = require('fs')
+const path = require('path')
+const utils = require('./utils')
+const { createHash } = require('crypto')
+const { homedir } = require('os')
 
 async function parseModelfile(ollama, modelfile, mfDir = process.cwd()) {
-  const out = [];
-  const lines = modelfile.split('\n');
+  const out = []
+  const lines = modelfile.split('\n')
   for (const line of lines) {
-    const [command, args] = line.split(' ', 2);
+    const [command, args] = line.split(' ', 2)
     if (['FROM', 'ADAPTER'].includes(command.toUpperCase())) {
-      const resolvedPath = resolvePath(args.trim(), mfDir);
+      const resolvedPath = resolvePath(args.trim(), mfDir)
       if (await fileExists(resolvedPath)) {
-        out.push(`${command} @${await createBlob(ollama, resolvedPath)}`);
+        out.push(`${command} @${await createBlob(ollama, resolvedPath)}`)
       } else {
-        out.push(`${command} ${args}`);
+        out.push(`${command} ${args}`)
       }
     } else {
-      out.push(line);
+      out.push(line)
     }
   }
-  return out.join('\n');
+  return out.join('\n')
 }
 
 function resolvePath(inputPath, mfDir) {
   if (inputPath.startsWith('~')) {
-    return path.join(homedir(), inputPath.slice(1));
+    return path.join(homedir(), inputPath.slice(1))
   }
-  return path.resolve(mfDir, inputPath);
+  return path.resolve(mfDir, inputPath)
 }
 
 async function fileExists(filePath) {
   try {
-    await fs.promises.access(filePath);
-    return true;
+    await fs.promises.access(filePath)
+    return true
   } catch {
-    return false;
+    return false
   }
 }
 
 async function createBlob(ollama, filePath) {
   if (typeof ReadableStream === 'undefined') {
-    throw new Error('Streaming uploads are not supported in this environment.');
+    throw new Error('Streaming uploads are not supported in this environment.')
   }
 
-  const fileStream = fs.createReadStream(filePath);
+  const fileStream = fs.createReadStream(filePath)
   const sha256sum = await new Promise((resolve, reject) => {
-    const hash = createHash('sha256');
-    fileStream.on('data', (data) => hash.update(data));
-    fileStream.on('end', () => resolve(hash.digest('hex')));
-    fileStream.on('error', reject);
-  });
+    const hash = createHash('sha256')
+    fileStream.on('data', (data) => hash.update(data))
+    fileStream.on('end', () => resolve(hash.digest('hex')))
+    fileStream.on('error', reject)
+  })
 
-  const digest = `sha256:${sha256sum}`;
+  const digest = `sha256:${sha256sum}`
 
   try {
-    await utils.head(
-      ollama.fetch,
-      `${ollama.config.host}/api/blobs/${digest}`,
-      { signal: ollama.abortController.signal },
-    );
+    await utils.head(ollama.fetch, `${ollama.config.host}/api/blobs/${digest}`, {
+      signal: ollama.abortController.signal,
+    })
   } catch (e) {
     if (e instanceof Error && e.message.includes('404')) {
       const readableStream = new ReadableStream({
         start(controller) {
           fileStream.on('data', (chunk) => {
-            controller.enqueue(chunk);
-          });
+            controller.enqueue(chunk)
+          })
 
           fileStream.on('end', () => {
-            controller.close();
-          });
+            controller.close()
+          })
 
           fileStream.on('error', (err) => {
-            controller.error(err);
-          });
+            controller.error(err)
+          })
         },
-      });
+      })
 
       await utils.post(
         ollama.fetch,
         `${ollama.config.host}/api/blobs/${digest}`,
         readableStream,
         { signal: ollama.abortController.signal },
-      );
+      )
     } else {
-      throw e;
+      throw e
     }
   }
 
-  return digest;
+  return digest
 }
 
 export async function readModelfile(ollama, request) {
-    let modelfileContent = ''
-    if (request.path) {
-      modelfileContent = await fs.promises.readFile(request.path, { encoding: 'utf8' })
-      modelfileContent = await parseModelfile(
-        ollama,
-        modelfileContent,
-        path.dirname(request.path),
-      )
-    } else if (request.modelfile) {
-      modelfileContent = await parseModelfile(ollama, request.modelfile)
-    } else {
-      throw new Error('Must provide either path or modelfile to create a model')
-    }
+  let modelfileContent = ''
+  if (request.path) {
+    modelfileContent = await fs.promises.readFile(request.path, { encoding: 'utf8' })
+    modelfileContent = await parseModelfile(
+      ollama,
+      modelfileContent,
+      path.dirname(request.path),
+    )
+  } else if (request.modelfile) {
+    modelfileContent = await parseModelfile(ollama, request.modelfile)
+  } else {
+    throw new Error('Must provide either path or modelfile to create a model')
+  }
 
-    return modelfileContent;
+  return modelfileContent
 }
 
 export async function readImage(imgPath) {

+ 6 - 4
src/utils.ts

@@ -51,10 +51,12 @@ function getPlatform() {
 }
 
 export const isNode = () => {
-  return typeof process !== "undefined" &&
-      process.versions != null &&
-      process.versions.node != null;
-};
+  return (
+    typeof process !== 'undefined' &&
+    process.versions != null &&
+    process.versions.node != null
+  )
+}
 
 const fetchWithHeaders = async (
   fetch: Fetch,