Compare commits
5 Commits
6255e3dbd5
...
main
Author | SHA1 | Date | |
---|---|---|---|
092c065809 | |||
e23a8d53d9 | |||
99ef24076e | |||
26c3cc79d7 | |||
c3eb540261 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -58,3 +58,8 @@ htmlcov/
|
|||||||
!.yarn/releases
|
!.yarn/releases
|
||||||
!.yarn/sdks
|
!.yarn/sdks
|
||||||
!.yarn/versions
|
!.yarn/versions
|
||||||
|
backend-rust/owlynews.sqlite3
|
||||||
|
backend-rust/target
|
||||||
|
/backend-rust/config.toml
|
||||||
|
/backend-rust/owlynews.sqlite3-shm
|
||||||
|
/backend-rust/owlynews.sqlite3-wal
|
||||||
|
@@ -2,7 +2,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
DB_PATH = Path(os.getenv("DB_NAME", "owlynews.sqlite3"))
|
DB_PATH = Path(os.getenv("DB_NAME", os.path.join(os.path.dirname(os.path.dirname(__file__)), "owlynews.sqlite3")))
|
||||||
OLLAMA_HOST = os.getenv("OLLAMA_HOST", "http://localhost:11434")
|
OLLAMA_HOST = os.getenv("OLLAMA_HOST", "http://localhost:11434")
|
||||||
MIN_CRON_HOURS = float(os.getenv("MIN_CRON_HOURS", 0.5))
|
MIN_CRON_HOURS = float(os.getenv("MIN_CRON_HOURS", 0.5))
|
||||||
DEFAULT_CRON_HOURS = float(os.getenv("CRON_HOURS", MIN_CRON_HOURS))
|
DEFAULT_CRON_HOURS = float(os.getenv("CRON_HOURS", MIN_CRON_HOURS))
|
||||||
@@ -12,7 +12,7 @@ LLM_MODEL = os.getenv("LLM_MODEL", "mistral-nemo:12b")
|
|||||||
LLM_TIMEOUT_SECONDS = int(os.getenv("LLM_TIMEOUT_SECONDS", 180))
|
LLM_TIMEOUT_SECONDS = int(os.getenv("LLM_TIMEOUT_SECONDS", 180))
|
||||||
OLLAMA_API_TIMEOUT_SECONDS = int(os.getenv("OLLAMA_API_TIMEOUT_SECONDS", 10))
|
OLLAMA_API_TIMEOUT_SECONDS = int(os.getenv("OLLAMA_API_TIMEOUT_SECONDS", 10))
|
||||||
ARTICLE_FETCH_TIMEOUT = int(os.getenv("ARTICLE_FETCH_TIMEOUT", 30))
|
ARTICLE_FETCH_TIMEOUT = int(os.getenv("ARTICLE_FETCH_TIMEOUT", 30))
|
||||||
MAX_ARTICLE_LENGTH = int(os.getenv("MAX_ARTICLE_LENGTH", 5000))
|
MAX_ARTICLE_LENGTH = int(os.getenv("MAX_ARTICLE_LENGTH", 10_000))
|
||||||
|
|
||||||
frontend_path = os.path.join(
|
frontend_path = os.path.join(
|
||||||
os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
|
os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
|
||||||
|
@@ -15,6 +15,8 @@ import sqlite3
|
|||||||
import time
|
import time
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Any, Dict, List, Union
|
from typing import Any, Dict, List, Union
|
||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
|
||||||
# Third-party imports
|
# Third-party imports
|
||||||
import httpx
|
import httpx
|
||||||
@@ -62,6 +64,19 @@ scheduler.add_job(
|
|||||||
scheduler.start()
|
scheduler.start()
|
||||||
|
|
||||||
|
|
||||||
|
def start_frontend_build():
|
||||||
|
try:
|
||||||
|
subprocess.Popen(
|
||||||
|
["yarn", "build"],
|
||||||
|
cwd="../frontend",
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
print("Frontend build started successfully")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to start frontend build: {e}")
|
||||||
|
|
||||||
|
|
||||||
# API endpoints
|
# API endpoints
|
||||||
@app.get("/news", response_model=List[Dict[str, Any]])
|
@app.get("/news", response_model=List[Dict[str, Any]])
|
||||||
async def get_news(
|
async def get_news(
|
||||||
@@ -200,8 +215,6 @@ async def list_feeds(db: sqlite3.Cursor = Depends(get_db)):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/feeds", response_model=SuccessResponse)
|
@app.post("/feeds", response_model=SuccessResponse)
|
||||||
async def add_feed(
|
async def add_feed(
|
||||||
feed: FeedData,
|
feed: FeedData,
|
||||||
@@ -290,7 +303,7 @@ async def get_model_status():
|
|||||||
|
|
||||||
|
|
||||||
@app.post("/sync", response_model=None)
|
@app.post("/sync", response_model=None)
|
||||||
async def manual_sync(db: sqlite3.Cursor = Depends(get_db)):
|
async def manual_sync(db: sqlite3.Cursor = Depends(get_db_write)): # Note: changed to get_db_write
|
||||||
"""
|
"""
|
||||||
Manually trigger a feed synchronization.
|
Manually trigger a feed synchronization.
|
||||||
|
|
||||||
@@ -302,7 +315,14 @@ async def manual_sync(db: sqlite3.Cursor = Depends(get_db)):
|
|||||||
"""
|
"""
|
||||||
db.execute("SELECT val FROM meta WHERE key='last_sync'")
|
db.execute("SELECT val FROM meta WHERE key='last_sync'")
|
||||||
row = db.fetchone()
|
row = db.fetchone()
|
||||||
last_sync_ts = int(row["val"])
|
|
||||||
|
if row is None:
|
||||||
|
# Initialize the last_sync key if it doesn't exist
|
||||||
|
import time
|
||||||
|
last_sync_ts = int(time.time()) - (SYNC_COOLDOWN_MINUTES * 60 + 1) # Set to a time that allows sync
|
||||||
|
db.execute("INSERT INTO meta (key, val) VALUES ('last_sync', ?)", (str(last_sync_ts),))
|
||||||
|
else:
|
||||||
|
last_sync_ts = int(row["val"])
|
||||||
|
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
last_sync_time = datetime.fromtimestamp(last_sync_ts, timezone.utc)
|
last_sync_time = datetime.fromtimestamp(last_sync_ts, timezone.utc)
|
||||||
@@ -314,8 +334,12 @@ async def manual_sync(db: sqlite3.Cursor = Depends(get_db)):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
task = asyncio.create_task(NewsFetcher.harvest_feeds())
|
task = asyncio.create_task(NewsFetcher.harvest_feeds())
|
||||||
|
# Update the last_sync timestamp after triggering the sync
|
||||||
|
current_ts = int(time.time())
|
||||||
|
db.execute("UPDATE meta SET val=? WHERE key='last_sync'", (str(current_ts),))
|
||||||
return {"status": "triggered", "task_id": id(task)}
|
return {"status": "triggered", "task_id": id(task)}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Failed to trigger sync: {e}")
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
500, f"Failed to trigger sync: {str(e)}"
|
500, f"Failed to trigger sync: {str(e)}"
|
||||||
)
|
)
|
||||||
@@ -394,5 +418,11 @@ async def update_cron_schedule(
|
|||||||
return {"hours": hours}
|
return {"hours": hours}
|
||||||
|
|
||||||
|
|
||||||
# Mount static frontend
|
|
||||||
app.mount("/", StaticFiles(directory=frontend_path, html=True), name="static")
|
app.mount("/", StaticFiles(directory=frontend_path, html=True), name="static")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
threading.Thread(target=start_frontend_build, daemon=True).start()
|
||||||
|
|
||||||
|
import uvicorn
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
|
|
||||||
|
@@ -209,7 +209,7 @@ class NewsFetcher:
|
|||||||
"format": "json",
|
"format": "json",
|
||||||
"options": {
|
"options": {
|
||||||
"num_gpu": 1, # Force GPU usage
|
"num_gpu": 1, # Force GPU usage
|
||||||
"num_ctx": 8192, # Context size
|
"num_ctx": 128_000, # Context size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ SYNC_COOLDOWN_MINUTES=30
|
|||||||
LLM_MODEL=qwen2:7b-instruct-q4_K_M
|
LLM_MODEL=qwen2:7b-instruct-q4_K_M
|
||||||
LLM_MODEL=phi3:3.8b-mini-128k-instruct-q4_0
|
LLM_MODEL=phi3:3.8b-mini-128k-instruct-q4_0
|
||||||
LLM_MODEL=mistral-nemo:12b
|
LLM_MODEL=mistral-nemo:12b
|
||||||
|
LLM_MODEL=cnjack/mistral-samll-3.1:24b-it-q4_K_S
|
||||||
|
|
||||||
# Timeout in seconds for LLM requests
|
# Timeout in seconds for LLM requests
|
||||||
LLM_TIMEOUT_SECONDS=180
|
LLM_TIMEOUT_SECONDS=180
|
||||||
|
@@ -23,7 +23,11 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/news': 'http://localhost:8000',
|
'/news': 'http://localhost:8000',
|
||||||
'/meta': 'http://localhost:8000'
|
'/meta': 'http://localhost:8000',
|
||||||
|
'/feeds': 'http://localhost:8000',
|
||||||
|
'/model': 'http://localhost:8000',
|
||||||
|
'/sync': 'http://localhost:8000',
|
||||||
|
'/settings': 'http://localhost:8000'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user