Skip to content

Commit c05f6e5

Browse files
committed
restructure code
1 parent 9b6a2bc commit c05f6e5

File tree

16 files changed

+99
-61
lines changed

16 files changed

+99
-61
lines changed

electron/main.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ const os = require("os");
77

88
// Create or append to a log file in the user's home directory
99
const logPath = path.join(os.homedir(), "jaaz-log.txt");
10+
// Check if the log file exists and delete it
11+
if (fs.existsSync(logPath)) {
12+
fs.unlinkSync(logPath);
13+
}
14+
1015
const logStream = fs.createWriteStream(logPath, { flags: "a" });
1116

1217
// Redirect all stdout and stderr to the log file

react/src/Settings.tsx

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type LLMConfig = {
1717
models: Record<string, { type?: "text" | "image" | "video" }>;
1818
url: string;
1919
api_key: string;
20-
max_tokens: number;
20+
max_tokens?: number;
2121
};
2222

2323
const PROVIDER_NAME_MAPPING: { [key: string]: { name: string; icon: string } } =
@@ -35,6 +35,10 @@ const PROVIDER_NAME_MAPPING: { [key: string]: { name: string; icon: string } } =
3535
name: "Ollama",
3636
icon: "https://images.seeklogo.com/logo-png/59/1/ollama-logo-png_seeklogo-593420.png",
3737
},
38+
huggingface: {
39+
name: "Hugging Face",
40+
icon: "https://huggingface.co/favicon.ico",
41+
},
3842
};
3943
const DEFAULT_CONFIG: { [key: string]: LLMConfig } = {
4044
anthropic: {
@@ -67,6 +71,13 @@ const DEFAULT_CONFIG: { [key: string]: LLMConfig } = {
6771
api_key: "",
6872
max_tokens: 8192,
6973
},
74+
// huggingface: {
75+
// models: {
76+
// "dreamlike-art/dreamlike-photoreal-2.0": { type: "image" },
77+
// },
78+
// url: "https://api.replicate.com/v1/",
79+
// api_key: "",
80+
// },
7081
ollama: {
7182
models: {},
7283
url: "http://localhost:11434",
@@ -215,29 +226,30 @@ export default function Settings() {
215226
Your API key will be stored securely
216227
</p>
217228
</div>
218-
219-
<div className="space-y-2">
220-
<Label htmlFor={`${key}-maxTokens`}>Max Tokens</Label>
221-
<Input
222-
id={`${key}-maxTokens`}
223-
type="number"
224-
placeholder="Enter your max tokens"
225-
value={config[key]?.max_tokens ?? 8192}
226-
onChange={(e) =>
227-
setConfig({
228-
...config,
229-
[key]: {
230-
...config[key],
231-
max_tokens: parseInt(e.target.value),
232-
},
233-
})
234-
}
235-
className="w-full"
236-
/>
237-
<p className="text-xs text-gray-500">
238-
The maximum number of tokens in the response
239-
</p>
240-
</div>
229+
{key !== "replicate" && key !== "huggingface" && (
230+
<div className="space-y-2">
231+
<Label htmlFor={`${key}-maxTokens`}>Max Tokens</Label>
232+
<Input
233+
id={`${key}-maxTokens`}
234+
type="number"
235+
placeholder="Enter your max tokens"
236+
value={config[key]?.max_tokens ?? 8192}
237+
onChange={(e) =>
238+
setConfig({
239+
...config,
240+
[key]: {
241+
...config[key],
242+
max_tokens: parseInt(e.target.value),
243+
},
244+
})
245+
}
246+
className="w-full"
247+
/>
248+
<p className="text-xs text-gray-500">
249+
The maximum number of tokens in the response
250+
</p>
251+
</div>
252+
)}
241253
<p>
242254
Models ({Object.keys(config[key]?.models ?? {}).length ?? 0})
243255
</p>

server/main.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import io
55
import argparse
66
from contextlib import asynccontextmanager
7-
from localmanus.services.db_service import DatabaseService
87

98
root_dir = os.path.dirname(__file__)
109

@@ -15,7 +14,7 @@
1514
from fastapi import FastAPI
1615
from fastapi.staticfiles import StaticFiles
1716
from fastapi.responses import FileResponse
18-
from localmanus.routers import config, agent, workspace, image_tools
17+
from routers import config, agent, workspace, image_tools
1918

2019
@asynccontextmanager
2120
async def lifespan(app: FastAPI):
Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
import asyncio
1212
import aiohttp
1313
import requests
14-
from localmanus.services.agent_service import openai_client, anthropic_client, ollama_client
15-
from localmanus.services.mcp import MCPClient
16-
from localmanus.services.config_service import config_service, app_config, USER_DATA_DIR
14+
from services.agent_service import openai_client, anthropic_client, ollama_client
15+
from services.mcp import MCPClient
16+
from services.config_service import config_service, app_config, USER_DATA_DIR
1717
from starlette.websockets import WebSocketDisconnect
18-
from localmanus.services.db_service import db_service
19-
from localmanus.routers.image_tools import generate_image
20-
from localmanus.routers.websocket import active_websockets, send_to_websocket
18+
from services.db_service import db_service
19+
from routers.image_tools import generate_image
20+
from routers.websocket import active_websockets, send_to_websocket
2121

2222
llm_config = config_service.get_config()
2323

@@ -158,7 +158,7 @@ async def chat_openai(messages: list, session_id: str, text_model: dict, image_m
158158
if line_str.startswith('data: {'):
159159
line_str = line_str[6:] # Remove "data: " prefix
160160
chunk = json.loads(line_str) # Parse the JSON
161-
print('👇 chunk:', chunk)
161+
# print('👇 chunk:', chunk)
162162
# Extract content from the choices array
163163
if 'choices' in chunk and len(chunk['choices']) > 0:
164164
delta = chunk['choices'][0].get('delta', {})
@@ -374,6 +374,12 @@ async def chat(request: Request):
374374
session_id = data.get('session_id')
375375
text_model = data.get('text_model')
376376
image_model = data.get('image_model')
377+
print('👇app_config.get("system_prompt", "")', app_config.get('system_prompt', ''))
378+
if app_config.get('system_prompt', ''):
379+
messages.append({
380+
'role': 'system',
381+
'content': app_config.get('system_prompt', '')
382+
})
377383

378384
if len(messages) == 1:
379385
# create new session
@@ -477,7 +483,7 @@ async def get_models():
477483
for model_name in models:
478484
if provider == 'ollama':
479485
continue
480-
if config[provider].get('api_key', '') == '':
486+
if provider != 'huggingface' and config[provider].get('api_key', '') == '':
481487
continue
482488
model = models[model_name]
483489
res.append({
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from fastapi import APIRouter, Request
2-
from localmanus.services.config_service import config_service
3-
from localmanus.services.agent_service import llm_reload_clients
2+
from services.config_service import config_service
3+
from services.agent_service import llm_reload_clients
44

55
router = APIRouter(prefix="/api/config")
66

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import base64
22
from fastapi.responses import FileResponse
33
import requests
4-
from localmanus.services.config_service import app_config
4+
from services.config_service import app_config
55
import traceback
66
import time
7-
from localmanus.services.config_service import USER_DATA_DIR, FILES_DIR
8-
from localmanus.routers.websocket import send_to_websocket
7+
from services.config_service import USER_DATA_DIR, FILES_DIR
8+
from routers.websocket import send_to_websocket
99
from PIL import Image
1010
from io import BytesIO
1111
import os
@@ -60,13 +60,12 @@ async def generate_image(args_json: dict, ctx: dict):
6060
prompt: str = args_json.get('prompt', '')
6161
aspect_ratio: str = args_json.get('aspect_ratio', '1:1')
6262
input_image: str = args_json.get('input_image', '')
63-
api_key = app_config.get('replicate', {}).get('api_key', '')
63+
6464
if prompt == '':
6565
raise ValueError("Image generation failed: text prompt is required")
6666
if model == '':
6767
raise ValueError("Image generation failed: model is not selected")
68-
if not api_key:
69-
raise ValueError("Image generation failed: Replicate API key is not set")
68+
7069
if input_image:
7170
# hardcode kontext model for image editing for now
7271
model = 'black-forest-labs/flux-kontext-pro'
@@ -79,6 +78,26 @@ async def generate_image(args_json: dict, ctx: dict):
7978
if not mime_type:
8079
mime_type = "image/png" # default fallback
8180
input_image = f"data:{mime_type};base64,{b64}"
81+
mime_type, width, height, filename = await generate_image_replicate(prompt, model, aspect_ratio, input_image)
82+
await send_to_websocket(session_id, {
83+
'type': 'image_generated',
84+
'image_data': {
85+
'mime_type': mime_type,
86+
'url': f'/api/file/{filename}',
87+
'width': width,
88+
'height': height,
89+
}
90+
})
91+
return [
92+
{
93+
'role': 'tool',
94+
'content': f'Image generation successful: ![image filename: {filename}](/api/file/{filename})',
95+
}
96+
]
97+
async def generate_image_replicate(prompt, model, aspect_ratio, input_image):
98+
api_key = app_config.get('replicate', {}).get('api_key', '')
99+
if not api_key:
100+
raise ValueError("Image generation failed: Replicate API key is not set")
82101
url = f"https://api.replicate.com/v1/models/{model}/predictions"
83102
headers = {
84103
"Authorization": f"Bearer {api_key}",
@@ -109,21 +128,19 @@ async def generate_image(args_json: dict, ctx: dict):
109128
# get image dimensions
110129
mime_type, width, height, extension = await get_image_info_and_save(output, os.path.join(FILES_DIR, f'{image_id}'))
111130
filename = f'{image_id}.{extension}'
112-
await send_to_websocket(session_id, {
113-
'type': 'image_generated',
114-
'image_data': {
115-
'mime_type': mime_type,
116-
'url': f'/api/file/{filename}',
117-
'width': width,
118-
'height': height,
119-
}
120-
})
121-
return [
122-
{
123-
'role': 'tool',
124-
'content': f'Image generation successful: ![image filename: {filename}]({output})',
125-
}
126-
]
131+
return mime_type, width, height, filename
132+
133+
async def generate_image_huggingface(args_json: dict, ctx: dict):
134+
from diffusers.pipelines.auto_pipeline import AutoPipelineForText2Image
135+
import torch
136+
137+
pipe_txt2img = AutoPipelineForText2Image.from_pretrained(
138+
"dreamlike-art/dreamlike-photoreal-2.0", torch_dtype=torch.float16, use_safetensors=True
139+
).to("cuda")
140+
141+
prompt = "cinematic photo of Godzilla eating sushi with a cat in a izakaya, 35mm photograph, film, professional, 4k, highly detailed"
142+
generator = torch.Generator(device="cpu").manual_seed(37)
143+
image = pipe_txt2img(prompt, generator=generator).images[0]
127144

128145
async def get_image_info_and_save(url, file_path_without_extension):
129146
# Fetch the image asynchronously

0 commit comments

Comments
 (0)