Tsitsi19 commited on
Commit
de2a5d3
·
verified ·
1 Parent(s): c8c6716

Initial deployment of Claude Code with Qwen local

Browse files
Files changed (2) hide show
  1. Dockerfile +51 -0
  2. app_claude_code_gui.py +81 -0
Dockerfile ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.12-slim
2
+
3
+ # Install system dependencies
4
+ RUN apt-get update && apt-get install -y \
5
+ git \
6
+ curl \
7
+ build-essential \
8
+ libopenblas-dev \
9
+ nodejs \
10
+ npm \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ # Set working directory
14
+ WORKDIR /app
15
+
16
+ # Install llama-cpp-python with OpenBLAS and Streamlit
17
+ RUN pip install llama-cpp-python[server] streamlit
18
+
19
+ # Download Qwen2.5-Coder-7B-Instruct GGUF Q4_K_M
20
+ RUN mkdir -p /app/models
21
+ RUN curl -L -o /app/models/qwen2.5-coder-7b-instruct-q4_k_m.gguf https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct-GGUF/resolve/main/qwen2.5-coder-7b-instruct-q4_k_m.gguf
22
+
23
+ # Install Claude Code globally
24
+ RUN npm install -g @anthropic-ai/claude-code
25
+
26
+ # Create a dummy config.toml for OpenManus if needed, or just for structure
27
+ RUN mkdir -p /app/config
28
+ RUN echo '[llm]\nmodel = "qwen2.5-coder"\nbase_url = "http://localhost:8001/v1"\napi_key = "not-needed"\nmax_tokens = 4096\ntemperature = 0.0\n' > /app/config/config.toml
29
+
30
+ # Copy our custom GUI for Claude Code
31
+ COPY app_claude_code_gui.py /app/app_claude_code_gui.py
32
+
33
+ # Create a startup script
34
+ RUN echo '#!/bin/bash\n\
35
+ # Start llama-cpp-python server in background\n\
36
+ python3 -m llama_cpp.server --model /app/models/qwen2.5-coder-7b-instruct-q4_k_m.gguf --host 0.0.0.0 --port 8001 &\n\
37
+ \n\
38
+ # Wait for server to start\n\
39
+ until curl -s http://localhost:8001/v1/models > /dev/null; do\n\
40
+ echo "Waiting for LLM server..."\n\
41
+ sleep 5\n\
42
+ done\n\
43
+ \n\
44
+ # Start Streamlit UI on port 7860\n\
45
+ streamlit run app_claude_code_gui.py --server.port 7860 --server.address 0.0.0.0\n\
46
+ ' > /app/start.sh && chmod +x /app/start.sh
47
+
48
+ # HF Spaces expect a web service on port 7860
49
+ EXPOSE 7860
50
+
51
+ CMD ["/app/start.sh"]
app_claude_code_gui.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import subprocess
3
+ import threading
4
+ import queue
5
+ import os
6
+ import sys
7
+
8
+ st.set_page_config(page_title="Claude Code Web UI", page_icon="💻")
9
+
10
+ st.title("💻 Claude Code Web UI")
11
+ st.markdown("Interface pour Claude Code avec Qwen2.5-Coder-7B (GGUF) local")
12
+
13
+ if "messages" not in st.session_state:
14
+ st.session_state.messages = []
15
+
16
+ for message in st.session_state.messages:
17
+ with st.chat_message(message["role"]):
18
+ st.markdown(message["content"])
19
+
20
+ def run_command(command, output_queue):
21
+ try:
22
+ # Ensure claude-code uses the local LLM server
23
+ # This might require setting environment variables or a config file for claude-code
24
+ # For now, we assume claude-code can be configured to use an OpenAI-compatible endpoint
25
+ # or we might need to modify its source/config if it's hardcoded to Anthropic API.
26
+ # Let's try to pass the API base URL as an environment variable if claude-code supports it.
27
+ # If not, this part will need further investigation.
28
+
29
+ # For demonstration, we'll just run a simple command or simulate claude-code interaction.
30
+ # A real integration would involve piping input/output to the claude-code CLI.
31
+
32
+ # Example: running a simple shell command for now
33
+ process = subprocess.Popen(
34
+ command,
35
+ shell=True,
36
+ stdout=subprocess.PIPE,
37
+ stderr=subprocess.STDOUT,
38
+ text=True,
39
+ bufsize=1
40
+ )
41
+ for line in iter(process.stdout.readline, ""):
42
+ output_queue.put(line)
43
+ process.stdout.close()
44
+ process.wait()
45
+ output_queue.put(None)
46
+ except Exception as e:
47
+ output_queue.put(f"Error executing command: {e}\n")
48
+ output_queue.put(None)
49
+
50
+ if prompt := st.chat_input("Entrez votre commande Claude Code ou shell..."):
51
+ st.session_state.messages.append({"role": "user", "content": prompt})
52
+ with st.chat_message("user"):
53
+ st.markdown(prompt)
54
+
55
+ with st.chat_message("assistant"):
56
+ response_placeholder = st.empty()
57
+ full_response = ""
58
+ output_queue = queue.Queue()
59
+
60
+ # Prepend 'claude-code' if the user just types a command, or allow direct shell commands
61
+ command_to_run = prompt
62
+ if not prompt.startswith("claude-code") and not prompt.startswith("npm") and not prompt.startswith("python") and not prompt.startswith("ls"):
63
+ command_to_run = f"claude-code {prompt}"
64
+
65
+ thread = threading.Thread(target=run_command, args=([command_to_run], output_queue))
66
+ thread.start()
67
+
68
+ while True:
69
+ try:
70
+ line = output_queue.get(timeout=1)
71
+ if line is None:
72
+ break
73
+ full_response += line
74
+ response_placeholder.markdown(f"```bash\n{full_response}▌\n```")
75
+ except queue.Empty:
76
+ if not thread.is_alive():
77
+ break
78
+ continue
79
+
80
+ response_placeholder.markdown(f"```bash\n{full_response}\n```")
81
+ st.session_state.messages.append({"role": "assistant", "content": full_response})