unlimited-ocr-mcp/ocr-posted.py
Jarian Cottingham c0fa9148b7 Initial commit: Unlimited-OCR MCP for opencode
- mcp/server.py: stdlib-only MCP stdio server exposing ocr_image tool
- ocr-posted.py: CLI equivalent (DB lookup + OCR via llama-server)
- run-ocr-server.sh: llama-server launcher with tuned sampling flags
- skill/ocr-image: opencode skill teaching agents when/how to call it
- README with full install instructions
2026-08-22 06:24:57 +00:00

77 lines
2.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""Pull an image posted into opencode from opencode.db and OCR it via local Unlimited-OCR.
Usage:
ocr-posted.py [latest | <part_id> | session:<session_id>] [prompt] [save_path]
Defaults: latest image part in DB, prompt "Free OCR."
Env: OPENCODE_DB, OCR_URL
"""
import base64, json, os, sqlite3, sys, urllib.request
DB = os.environ.get(
'OPENCODE_DB',
os.path.expanduser('~/.local/share/opencode/opencode.db'))
OCR_URL = os.environ.get('OCR_URL', 'http://127.0.0.1:11436/v1/chat/completions')
def pick(db, arg):
cur = db.cursor()
if arg and arg != 'latest':
if arg.startswith('session:'):
cur.execute(
"SELECT id, data FROM part WHERE session_id=? "
"AND data LIKE '%\"type\":\"file\"%' AND data LIKE '%data:image%' "
"ORDER BY time_created DESC LIMIT 1",
(arg[8:],))
else:
cur.execute("SELECT id, data FROM part WHERE id=?", (arg,))
else:
cur.execute(
"SELECT id, data FROM part WHERE data LIKE '%\"type\":\"file\"%' "
"AND data LIKE '%data:image%' ORDER BY time_created DESC LIMIT 1")
return cur.fetchone()
def ocr(data_url, prompt, max_tokens=4096):
body = json.dumps({
"model": "Unlimited-OCR",
"temperature": 0,
"repeat_penalty": 1.2,
"max_tokens": max_tokens,
"messages": [{"role": "user", "content": [
{"type": "image_url", "image_url": {"url": data_url}},
{"type": "text", "text": prompt},
]}],
}).encode()
req = urllib.request.Request(
OCR_URL, data=body, headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=1800) as r:
return json.load(r)['choices'][0]['message']['content']
def main():
arg = sys.argv[1] if len(sys.argv) > 1 else 'latest'
prompt = sys.argv[2] if len(sys.argv) > 2 else 'Free OCR.'
save = sys.argv[3] if len(sys.argv) > 3 else None
db = sqlite3.connect(DB, timeout=10)
row = pick(db, arg)
if not row:
print('no image part found', file=sys.stderr)
sys.exit(1)
pid, data = row
d = json.loads(data)
url = d.get('url', '')
if not url.startswith('data:image'):
print('part %s has no inline image data' % pid, file=sys.stderr)
sys.exit(1)
if save:
open(save, 'wb').write(base64.b64decode(url.split(',', 1)[1]))
print('saved image to %s' % save, file=sys.stderr)
print('part: %s file: %s' % (pid, d.get('filename')), file=sys.stderr)
print(ocr(url, prompt))
if __name__ == '__main__':
main()