github-actions[bot] commited on
Commit
fc2f017
·
1 Parent(s): b9a1550

Auto-deploy from GitHub: 141b37810ab063d3984b529a3bda3b8633f874c3

Browse files
Dockerfile CHANGED
@@ -14,11 +14,16 @@ RUN apt-get update && apt-get install -y \
14
  build-essential \
15
  zstd \
16
  procps \
 
 
17
  && rm -rf /var/lib/apt/lists/*
18
 
19
  # Install Ollama
20
  RUN curl -fsSL https://ollama.com/install.sh | sh
21
 
 
 
 
22
  # Copy project files
23
  COPY pyproject.toml .
24
  COPY . .
 
14
  build-essential \
15
  zstd \
16
  procps \
17
+ nodejs \
18
+ npm \
19
  && rm -rf /var/lib/apt/lists/*
20
 
21
  # Install Ollama
22
  RUN curl -fsSL https://ollama.com/install.sh | sh
23
 
24
+ # Install opencode CLI
25
+ RUN npm install -g opencode-ai
26
+
27
  # Copy project files
28
  COPY pyproject.toml .
29
  COPY . .
app/api/routes.py CHANGED
@@ -22,8 +22,9 @@ async def submit_task(request: Request):
22
  input_text = data['text'].strip()
23
  system_prompt = data.get('system_prompt', '').strip() or None
24
  hide_from_ui = 1 if data.get('hide_from_ui') else 0
 
25
 
26
- await crud.insert_task(task_id, input_text, system_prompt, 'not_started', hide_from_ui)
27
 
28
  await start_worker()
29
 
@@ -31,6 +32,7 @@ async def submit_task(request: Request):
31
  'id': task_id,
32
  'filename': input_text[:50] + ("..." if len(input_text) > 50 else ""),
33
  'status': 'not_started',
 
34
  'message': 'Task submitted successfully'
35
  })
36
 
 
22
  input_text = data['text'].strip()
23
  system_prompt = data.get('system_prompt', '').strip() or None
24
  hide_from_ui = 1 if data.get('hide_from_ui') else 0
25
+ model = (data.get('model') or 'qwen').strip().lower()
26
 
27
+ await crud.insert_task(task_id, input_text, system_prompt, 'not_started', hide_from_ui, model)
28
 
29
  await start_worker()
30
 
 
32
  'id': task_id,
33
  'filename': input_text[:50] + ("..." if len(input_text) > 50 else ""),
34
  'status': 'not_started',
35
+ 'model': model,
36
  'message': 'Task submitted successfully'
37
  })
38
 
app/db/crud.py CHANGED
@@ -3,14 +3,14 @@ from datetime import datetime, timedelta
3
  from app.core.config import settings
4
  from custom_logger import logger_config as logger
5
 
6
- async def insert_task(task_id: str, input_text: str, system_prompt: str, status: str, hide_from_ui: int):
7
  async with aiosqlite.connect(settings.DATABASE_FILE) as db:
8
  await db.execute('''INSERT INTO text_tasks
9
- (id, input_text, system_prompt, status, created_at, hide_from_ui)
10
- VALUES (?, ?, ?, ?, ?, ?)''',
11
- (task_id, input_text, system_prompt, status, datetime.now().isoformat(), hide_from_ui))
12
  await db.commit()
13
- logger.debug(f"Inserted task (ID: {task_id}) into database.")
14
 
15
  async def update_status(task_id: str, status: str, result: str = None, error: str = None):
16
  async with aiosqlite.connect(settings.DATABASE_FILE) as db:
 
3
  from app.core.config import settings
4
  from custom_logger import logger_config as logger
5
 
6
+ async def insert_task(task_id: str, input_text: str, system_prompt: str, status: str, hide_from_ui: int, model: str = 'qwen'):
7
  async with aiosqlite.connect(settings.DATABASE_FILE) as db:
8
  await db.execute('''INSERT INTO text_tasks
9
+ (id, input_text, system_prompt, model, status, created_at, hide_from_ui)
10
+ VALUES (?, ?, ?, ?, ?, ?, ?)''',
11
+ (task_id, input_text, system_prompt, model, status, datetime.now().isoformat(), hide_from_ui))
12
  await db.commit()
13
+ logger.debug(f"Inserted task (ID: {task_id}, model: {model}) into database.")
14
 
15
  async def update_status(task_id: str, status: str, result: str = None, error: str = None):
16
  async with aiosqlite.connect(settings.DATABASE_FILE) as db:
app/db/database.py CHANGED
@@ -9,6 +9,7 @@ async def init_db():
9
  (id TEXT PRIMARY KEY,
10
  input_text TEXT NOT NULL,
11
  system_prompt TEXT,
 
12
  status TEXT NOT NULL,
13
  result TEXT,
14
  created_at TEXT NOT NULL,
 
9
  (id TEXT PRIMARY KEY,
10
  input_text TEXT NOT NULL,
11
  system_prompt TEXT,
12
+ model TEXT DEFAULT 'qwen',
13
  status TEXT NOT NULL,
14
  result TEXT,
15
  created_at TEXT NOT NULL,
app/services/worker.py CHANGED
@@ -1,5 +1,7 @@
1
  import asyncio
2
  import json
 
 
3
  from app.core.config import settings
4
  from custom_logger import logger_config as logger
5
  from app.db import crud
@@ -33,9 +35,7 @@ async def worker_loop():
33
  await loop.run_in_executor(None, lambda: initiate({'text': 'Hi', 'model': 'qwen', 'max_new_tokens': 1}))
34
  logger.info("✅ Qwen model ready. Monitoring for new tasks...")
35
  except Exception as e:
36
- logger.error(f" Failed to load model: {e}")
37
- worker_running = False
38
- return
39
 
40
  while worker_running:
41
  logger.debug("Worker loop iteration, checking for files...")
@@ -48,8 +48,9 @@ async def worker_loop():
48
  task_id = row['id']
49
  input_text = row['input_text']
50
  system_prompt = row['system_prompt'] or "You are a helpful assistant."
 
51
 
52
- logger.info(f"\n{'='*60}\nProcessing task: {task_id}\n📌 Input: {input_text[:100]}...\n{'='*60}")
53
 
54
  await crud.update_status(task_id, 'processing')
55
 
@@ -63,21 +64,31 @@ async def worker_loop():
63
 
64
  try:
65
  await crud.update_progress(task_id, 5, "Starting...")
66
-
67
- result = await loop.run_in_executor(None, lambda: initiate(
68
- {
69
- 'text': input_text,
70
- 'system_prompt': system_prompt,
71
- 'model': 'qwen',
72
- },
73
- progress_callback=progress_cb
74
- ))
75
 
76
- if result:
77
- logger.success(f"Successfully processed: {task_id}")
78
- await crud.update_status(task_id, 'completed', result=json.dumps(result))
 
 
 
 
 
 
79
  else:
80
- raise Exception("initiate() returned empty result")
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
  except Exception as e:
83
  logger.error(f"Failed to process {task_id}: {str(e)}")
@@ -89,3 +100,19 @@ async def worker_loop():
89
  except Exception as e:
90
  logger.error(f"Worker error: {str(e)}")
91
  await asyncio.sleep(settings.POLL_INTERVAL)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import asyncio
2
  import json
3
+ import subprocess
4
+ import shutil
5
  from app.core.config import settings
6
  from custom_logger import logger_config as logger
7
  from app.db import crud
 
35
  await loop.run_in_executor(None, lambda: initiate({'text': 'Hi', 'model': 'qwen', 'max_new_tokens': 1}))
36
  logger.info("✅ Qwen model ready. Monitoring for new tasks...")
37
  except Exception as e:
38
+ logger.warning(f"⚠️ Qwen model not available (opencode-only tasks will still work): {e}")
 
 
39
 
40
  while worker_running:
41
  logger.debug("Worker loop iteration, checking for files...")
 
48
  task_id = row['id']
49
  input_text = row['input_text']
50
  system_prompt = row['system_prompt'] or "You are a helpful assistant."
51
+ model = row.get('model', 'qwen')
52
 
53
+ logger.info(f"\n{'='*60}\nProcessing task: {task_id} (model: {model})\n📌 Input: {input_text[:100]}...\n{'='*60}")
54
 
55
  await crud.update_status(task_id, 'processing')
56
 
 
64
 
65
  try:
66
  await crud.update_progress(task_id, 5, "Starting...")
 
 
 
 
 
 
 
 
 
67
 
68
+ if model == 'opencode':
69
+ await crud.update_progress(task_id, 10, "Running opencode...")
70
+ result = await loop.run_in_executor(None, lambda: _run_opencode(input_text))
71
+ if result:
72
+ logger.success(f"Successfully processed (opencode): {task_id}")
73
+ await crud.update_progress(task_id, 100, "Completed")
74
+ await crud.update_status(task_id, 'completed', result=json.dumps({"response": result}))
75
+ else:
76
+ raise Exception("opencode returned empty result")
77
  else:
78
+ result = await loop.run_in_executor(None, lambda: initiate(
79
+ {
80
+ 'text': input_text,
81
+ 'system_prompt': system_prompt,
82
+ 'model': 'qwen',
83
+ },
84
+ progress_callback=progress_cb
85
+ ))
86
+
87
+ if result:
88
+ logger.success(f"Successfully processed: {task_id}")
89
+ await crud.update_status(task_id, 'completed', result=json.dumps(result))
90
+ else:
91
+ raise Exception("initiate() returned empty result")
92
 
93
  except Exception as e:
94
  logger.error(f"Failed to process {task_id}: {str(e)}")
 
100
  except Exception as e:
101
  logger.error(f"Worker error: {str(e)}")
102
  await asyncio.sleep(settings.POLL_INTERVAL)
103
+
104
+
105
+ def _run_opencode(text: str) -> str:
106
+ if not shutil.which('opencode'):
107
+ raise FileNotFoundError(
108
+ "opencode CLI not found. Install it from https://opencode.ai"
109
+ )
110
+ result = subprocess.run(
111
+ ['opencode', 'run', text],
112
+ capture_output=True,
113
+ text=True,
114
+ timeout=300
115
+ )
116
+ if result.returncode != 0:
117
+ raise RuntimeError(f"opencode failed: {result.stderr.strip()}")
118
+ return result.stdout.strip()
index.html CHANGED
@@ -783,12 +783,12 @@
783
  const doc = {
784
  base_url: window.location.origin,
785
  endpoints: [
786
- { method: "POST", path: "/api/tasks/upload", desc: "Submit text generation task", body: "{ text: string, system_prompt?: string }" },
787
  { method: "GET", path: "/api/tasks", desc: "List all tasks" },
788
  { method: "GET", path: "/api/tasks/{task_id}", desc: "Get task details & result" },
789
  { method: "GET", path: "/health", desc: "Service health" }
790
  ],
791
- example_usage: `curl -X POST -H 'Content-Type: application/json' -d '{"text": "Hello"}' ${window.location.origin}/api/tasks/upload`
792
  };
793
  UI.resultText.innerText = JSON.stringify(doc, null, 2);
794
  UI.modalTitle.innerText = "API Documentation";
 
783
  const doc = {
784
  base_url: window.location.origin,
785
  endpoints: [
786
+ { method: "POST", path: "/api/tasks/upload", desc: "Submit text generation task", body: "{ text: string, system_prompt?: string, model?: 'qwen' | 'opencode' }" },
787
  { method: "GET", path: "/api/tasks", desc: "List all tasks" },
788
  { method: "GET", path: "/api/tasks/{task_id}", desc: "Get task details & result" },
789
  { method: "GET", path: "/health", desc: "Service health" }
790
  ],
791
+ example_usage: `curl -X POST -H 'Content-Type: application/json' -d '{"text": "Hello"}' ${window.location.origin}/api/tasks/upload` + '\n' + `curl -X POST -H 'Content-Type: application/json' -d '{"text": "list files", "model": "opencode"}' ${window.location.origin}/api/tasks/upload`
792
  };
793
  UI.resultText.innerText = JSON.stringify(doc, null, 2);
794
  UI.modalTitle.innerText = "API Documentation";