tune-basic-toolset 0.1.21 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,6 +17,7 @@ Basic toolset for [Tune](https://github.com/iovdin/tune).
17
17
  - [search_tools](#search_tools) find appropriate tools for a task
18
18
  - [osa](#osa) manage reminders/notes/calendar (AppleScript/macOS)
19
19
  - [jina_r](#jina_r) fetch webpage content
20
+ - [mistral_ocr](#mistral_ocr) extract text from documents and images via OCR
20
21
  - [websearch](#websearch) search the web with web-enabled llms
21
22
  - [list](#list) keep list of tasks todo (loops for LLM)
22
23
  - [sqlite](#sqlite) execute sqlite queries
@@ -291,6 +292,40 @@ Tune is a versatile toolkit designed for developers and users to effectively int
291
292
  <cut for brevity>
292
293
  ```
293
294
 
295
+ ### `mistral_ocr`
296
+ Extract text from documents and images using the [Mistral OCR API](https://mistral.ai/). Requires a `MISTRAL_KEY` set in `.env`.
297
+
298
+ Supports documents: `.pdf`, `.docx`, `.pptx`, `.txt`, `.epub`, `.xml`, `.rtf`, `.odt`, `.bib`, `.fb2`, `.ipynb`, `.tex`, `.opml`, `.1`, `.man`
299
+
300
+ Supports images: `.jpg`, `.jpeg`, `.png`, `.avif`, `.tiff`, `.gif`, `.heic`, `.heif`, `.bmp`, `.webp`
301
+
302
+ ```chat
303
+ user: @mistral_ocr
304
+ extract text from invoice.pdf
305
+
306
+ tool_call: mistral_ocr {"filename":"invoice.pdf"}
307
+ tool_result:
308
+ # Invoice #1042
309
+
310
+ **Date:** 2024-04-01
311
+ **Bill To:** Acme Corp
312
+
313
+ | Description | Amount |
314
+ |--------------------|---------|
315
+ | Consulting (10h) | $1500 |
316
+ | Hosting (1 month) | $50 |
317
+ | **Total** | $1550 |
318
+
319
+ user:
320
+ what does this screenshot say?
321
+
322
+ tool_call: mistral_ocr {"filename":"screenshot.png"}
323
+ tool_result:
324
+ ## System Alert
325
+
326
+ Your disk usage has exceeded 90%. Please free up space to avoid performance issues.
327
+ ```
328
+
294
329
  ### `websearch`
295
330
  Search the web with web enabled llms
296
331
  Supports search with `perplexity/sonar`, `perplexity/sonar-pro`, `gpt-4o-search-preview`, `gpt-4o-mini-search-preview` models via the `model` parameter (defaults to `perplexity/sonar`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tune-basic-toolset",
3
- "version": "0.1.21",
3
+ "version": "0.2.0",
4
4
  "description": "Basic toolset for tune",
5
5
  "main": "src/index.js",
6
6
  "files": [
package/src/init.proc.js CHANGED
@@ -6,10 +6,7 @@ module.exports = async function init(node, args, ctx) {
6
6
 
7
7
  // include file
8
8
  if (content.indexOf("@") === 0) {
9
- return {
10
- type: "text",
11
- read: async () => this.read(content.replace(/^@{1,2}/, ""))
12
- }
9
+ return ctx.resolve(content.replace(/^@{1,2}/, ""))
13
10
  }
14
11
 
15
12
  return {
@@ -18,5 +18,5 @@
18
18
  },
19
19
  "required": ["url"]
20
20
  },
21
- "$escape_output": false
21
+ "$escape_output": true
22
22
  }
@@ -0,0 +1,14 @@
1
+ {
2
+ "description": "Perform OCR on a local files using Mistral OCR API, returns extracted text as markdown",
3
+ "parameters": {
4
+ "type": "object",
5
+ "properties": {
6
+ "filename": {
7
+ "type": "string",
8
+ "description": "Path to the file to perform OCR on"
9
+ }
10
+ },
11
+ "required": ["filename"]
12
+ },
13
+ "$escape_output": true
14
+ }
@@ -0,0 +1,79 @@
1
+ const path = require('path');
2
+
3
+ const MIME_TYPES = {
4
+ // Documents → document_url
5
+ '.pdf': { kind: 'document', mime: 'application/pdf' },
6
+ '.docx': { kind: 'document', mime: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' },
7
+ '.xlsx': { kind: 'document', mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' },
8
+ '.pptx': { kind: 'document', mime: 'application/vnd.openxmlformats-officedocument.presentationml.presentation' },
9
+ '.txt': { kind: 'document', mime: 'text/plain' },
10
+ '.epub': { kind: 'document', mime: 'application/epub+zip' },
11
+ '.xml': { kind: 'document', mime: 'application/xml' },
12
+ '.rtf': { kind: 'document', mime: 'application/rtf' },
13
+ '.odt': { kind: 'document', mime: 'application/vnd.oasis.opendocument.text' },
14
+ '.bib': { kind: 'document', mime: 'application/x-bibtex' },
15
+ '.fb2': { kind: 'document', mime: 'application/x-fictionbook+xml' },
16
+ '.ipynb': { kind: 'document', mime: 'application/x-ipynb+json' },
17
+ '.tex': { kind: 'document', mime: 'application/x-tex' },
18
+ '.opml': { kind: 'document', mime: 'text/x-opml' },
19
+ '.1': { kind: 'document', mime: 'application/x-troff-man' },
20
+ '.man': { kind: 'document', mime: 'application/x-troff-man' },
21
+ // Images → image_url
22
+ '.jpg': { kind: 'image', mime: 'image/jpeg' },
23
+ '.jpeg': { kind: 'image', mime: 'image/jpeg' },
24
+ '.png': { kind: 'image', mime: 'image/png' },
25
+ '.avif': { kind: 'image', mime: 'image/avif' },
26
+ '.tiff': { kind: 'image', mime: 'image/tiff' },
27
+ '.gif': { kind: 'image', mime: 'image/gif' },
28
+ '.heic': { kind: 'image', mime: 'image/heic' },
29
+ '.heif': { kind: 'image', mime: 'image/heif' },
30
+ '.bmp': { kind: 'image', mime: 'image/bmp' },
31
+ '.webp': { kind: 'image', mime: 'image/webp' },
32
+ };
33
+
34
+ module.exports = async function mistralOcr({ filename }, ctx) {
35
+ const apiKey = await ctx.read("MISTRAL_KEY");
36
+ if (!apiKey) {
37
+ throw new Error("MISTRAL_KEY is not set");
38
+ }
39
+
40
+ const ext = path.extname(filename).toLowerCase();
41
+ const typeInfo = MIME_TYPES[ext];
42
+ if (!typeInfo) {
43
+ throw new Error(`Unsupported file extension: ${ext}`);
44
+ }
45
+
46
+ const node = await ctx.resolve(filename)
47
+ if (!node) {
48
+ return `${filename} not found`
49
+ }
50
+ const buf = await node.read(true);
51
+ const base64 = buf.toString('base64');
52
+ const dataUrl = `data:${typeInfo.mime};base64,${base64}`;
53
+
54
+ const document = typeInfo.kind === 'image'
55
+ ? { type: 'image_url', image_url: dataUrl }
56
+ : { type: 'document_url', document_url: dataUrl };
57
+
58
+ const response = await fetch("https://api.mistral.ai/v1/ocr", {
59
+ method: "POST",
60
+ headers: {
61
+ "Content-Type": "application/json",
62
+ "Authorization": `Bearer ${apiKey}`,
63
+ },
64
+ body: JSON.stringify({
65
+ model: "mistral-ocr-latest",
66
+ document,
67
+ include_image_base64: true,
68
+ }),
69
+ });
70
+
71
+ if (!response.ok) {
72
+ const errorText = await response.text();
73
+ throw new Error(`Mistral OCR error: ${response.status} ${response.statusText} - ${errorText}`);
74
+ }
75
+
76
+ const result = await response.json();
77
+
78
+ return result.pages.map(page => page.markdown).join("\n\n");
79
+ };
@@ -5,11 +5,11 @@
5
5
  "properties": {
6
6
  "text": {
7
7
  "type": "string",
8
- "description": "The shell command to execute"
8
+ "description": "The shell command to execute, always prepend input with comment explaining the command so user that is not familiar with shell can understand and estimate how dangerous the command is"
9
9
  },
10
10
  "host": {
11
11
  "type": "string",
12
- "description": "remote host like user@host.com to execute the shell comand on (uses ssh)"
12
+ "description": "remote host like user@host.com to execute the shell comand on (uses ssh), default is empty (local shell) "
13
13
  }
14
14
  },
15
15
  "required": ["text"]
package/src/text.proc.js CHANGED
@@ -5,7 +5,7 @@ module.exports = async function(node, args, ctx) {
5
5
  return {
6
6
  ...node,
7
7
  type: "text",
8
- read : async () => ctx.read(node.fullname)
8
+ read : async () => ctx.read(node.fullname || node.name)
9
9
  }
10
10
 
11
11
  }