commit 355c76a8cba3699698c54dbcc2afd5ca229d43cb Author: narawat Date: Tue Sep 22 06:03:19 2026 +0700 1st commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1e3bad --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +mountvolume/ \ No newline at end of file diff --git a/app/.vscode/settings.json b/app/.vscode/settings.json new file mode 100755 index 0000000..9c03966 --- /dev/null +++ b/app/.vscode/settings.json @@ -0,0 +1,20 @@ +{ + "MicroPython.executeButton": [ + { + "text": "▶", + "tooltip": "Run", + "alignment": "left", + "command": "extension.executeFile", + "priority": 3.5 + } + ], + "MicroPython.syncButton": [ + { + "text": "$(sync)", + "tooltip": "sync", + "alignment": "left", + "command": "extension.execute", + "priority": 4 + } + ] +} \ No newline at end of file diff --git a/app/Dockerfile b/app/Dockerfile new file mode 100755 index 0000000..0fc7a4e --- /dev/null +++ b/app/Dockerfile @@ -0,0 +1,198 @@ +# FROM nvidia/cuda:12.5.1-devel-ubuntu22.04 +FROM nvidia/cuda:12.6.3-devel-ubuntu22.04 +# FROM julia:latest +# FROM debian:latest + + +# ------------------------------------------------------------------------------------------------ # +# install required APT packages # +# ------------------------------------------------------------------------------------------------ # +RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y \ + software-properties-common \ + build-essential \ + busybox \ + g++ \ + gcc \ + wget \ + net-tools \ + curl \ + iputils-ping \ + unzip \ + unixodbc \ + unixodbc-dev \ + libicu-dev \ + nano \ + libmosquitto-dev \ + git \ + git-lfs \ + ffmpeg \ + libsm6 \ + libxext6 \ + tar \ + zip \ + libssl-dev \ + cmake \ + ccache \ + # python3 \ + # python3-pip \ + # python-is-python3 \ + # python3-venv \ + postgresql-client \ + cargo \ + procps + +# ------------------------------------------------------------------------------------------------ # +# Set up folder structure # +# ------------------------------------------------------------------------------------------------ # +RUN mkdir /appfolder \ + && mkdir /appfolder/mountvolume \ + && mkdir /appfolder/app \ + && mkdir /appfolder/app/temp +# COPY . /appfolder/app/temp +# RUN mv /appfolder/app/temp/env_preparation.jl /appfolder/app + +# ------------------------------------------------------------------------------------------------ # +# Set up for Julia app # +# ------------------------------------------------------------------------------------------------ # +RUN mkdir /appfolder/julia +WORKDIR /appfolder/julia + + # Option 1) install julia by downloading the tar.gz file directly +# # RUN wget https://julialang-s3.julialang.org/bin/linux/x64/1.11/julia-1.11.3-linux-x86_64.tar.gz +# # RUN wget https://julialang-s3.julialang.org/bin/linux/aarch64/1.11/julia-1.11.3-linux-aarch64.tar.gz + +# COPY ./julia-1.11.3-linux-x86_64.tar.gz /appfolder/julia +# #COPY ./julia-1.11.3-linux-aarch64.tar.gz /appfolder/julia + +# RUN tar zxvf julia-1.11.3-linux-x86_64.tar.gz +# #RUN tar zxvf julia-1.11.3-linux-aarch64.tar.gz + +# RUN rm julia-1.11.3-linux-x86_64.tar.gz +# #RUN rm julia-1.11.3-linux-aarch64.tar.gz + +# # add julia executable softlink(symlink) to usr/local/bin to be able to run julia everywhere +# RUN ln -s /appfolder/julia/julia-1.11.3/bin/julia /usr/local/bin +# #RUN echo "alias julia='~/julia/julia-1.11.3/bin/julia'" >> ~/.bashrc && source ~/.bashrc + + + # Option 2) install julia by downloading the tar.gz file directly using juliaup +# RUN curl -fsSL https://install.julialang.org | sh -s -- -y +# SHELL ["/bin/bash", "--login" , "-c"] + + +# ------------------------------------------------------------------------------------------------ # +# Set up for webapp frontend # +# ------------------------------------------------------------------------------------------------ # +# # make folder path according to URL ***assuming root folder is at /appfolder/app as specified in Caddyfile*** +# RUN mkdir -p /appfolder/app/hq/agent/sommelier/frontend/user +# COPY . /appfolder/app/hq/agent/sommelier/frontend/user + +# -------------------------------------- install web server -------------------------------------- # + + # option 1) NGINX +# RUN apt-get update && apt-get install -y nginx +# COPY nginx.conf /etc/nginx/nginx.conf +# # Copy your static website files to the Nginx HTML directory +# COPY . /usr/share/nginx/html + + # option 2) Caddy +# RUN apt-get update && apt-get install -y caddy + +# ---------------------------------------- install backend --------------------------------------- # + + # option 1) nodejs https://deb.nodesource.com/ +# RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - && \ +# apt-get update && apt-get install -y nodejs + + +# ------------------------------------------------------------------------------------------------ # +# install Conda as primary python environment with specified python version # +# ------------------------------------------------------------------------------------------------ # +# WORKDIR /appfolder/app/temp +# RUN PATH="${HOME}/conda/bin:${PATH}" \ +# && wget -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" \ +# && bash Miniforge3.sh -b -p "${HOME}/conda" \ +# && rm -f Miniforge3.sh \ +# && source "${HOME}/conda/etc/profile.d/conda.sh" \ +# && source "${HOME}/conda/etc/profile.d/mamba.sh" \ +# && conda activate \ +# && conda update conda -y \ +# && conda install python=3.11 -y \ +# && conda install pip -y \ +# && pip install --trusted-host pypi.python.org -r /appfolder/app/temp/required_python_packages.txt +# # && . /root/.bashrc + +# # add python to path to allow CMD python -m at the last line AND to be able to call python -m at linux shell +# ENV PATH=/root/conda/bin:$PATH + +# # && CMAKE_ARGS="-DLLAMA_BLAS=ON -DLLAMA_BLAS_VENDOR=OpenBLAS -DLLAMA_AVX2=OFF -DLLAMA_F16C=OFF -DLLAMA_FMA=OFF" FORCE_CMAKE=1 pip install llama-cpp-python --no-cache-dir +# # && CMAKE_ARGS="-DLLAMA_CUBLAS=on -DLLAMA_AVX2=OFF -DLLAMA_F16C=OFF -DLLAMA_FMA=OFF" FORCE_CMAKE=1 pip install llama-cpp-python==0.2.77 --no-cache-dir +# # https://github.com/abetlen/llama-cpp-python/issues/412 old CPU cause CUBLAS compile problem + + +# ------------------------------------------------------------------------------------------------ # +# install julia package for my app # +# ------------------------------------------------------------------------------------------------ # +WORKDIR /appfolder/app +# RUN julia -t auto env_preparation.jl + + +# ------------------------------------------------------------------------------------------------ # +# install powershell # +# ------------------------------------------------------------------------------------------------ # +WORKDIR /appfolder/app/temp + + # Option 1) using .deb package +# RUN wget https://github.com/PowerShell/PowerShell/releases/download/v7.5.0/powershell_7.5.0-1.deb_amd64.deb +# RUN dpkg -i powershell_7.5.0-1.deb_amd64.deb +# RUN apt-get install -f + + # Option 2) using binary package +# https://learn.microsoft.com/en-us/powershell/scripting/install/install-other-linux?view=powershell-7.5 +# RUN wget https://github.com/PowerShell/PowerShell/releases/download/v7.5.2/powershell-7.5.2-linux-x64.tar.gz \ +# && mkdir -p /opt/microsoft/powershell/7 \ +# && tar -zxf powershell-7.5.2-linux-x64.tar.gz -C /opt/microsoft/powershell/7 \ +# && chmod +x /opt/microsoft/powershell/7/pwsh \ +# && ln -s /opt/microsoft/powershell/7/pwsh /usr/bin/pwsh + + +# ------------------------------------------------------------------------------------------------ # +# remove temp folder after everything is done # +# ------------------------------------------------------------------------------------------------ # +WORKDIR /appfolder/app +RUN rm -r /appfolder/app/temp + +# Make port 80 available to the world outside this container. If I use --publish at docker run command, there is no need to use EXPOSE +# EXPOSE 1883 + + +# ------------------------------------------------------------------------------------------------ # +# Build llama.cpp with CUDA # +# ------------------------------------------------------------------------------------------------ # +WORKDIR /appfolder/mountvolume/ +# clone llama.cpp repo +# RUN git clone https://github.com/ggml-org/llama.cpp + +# build llama.cpp (change directory in the same RUN command) didn't work in Dockerfile, I need to sleep the container then get inside +# to build it myself. +# WORKDIR /appfolder/mountvolume/llama.cpp +# RUN cmake -B build -DGGML_CUDA=ON +# RUN cmake --build build --config Release -j + + +# ------------------------------------------------------------------------------------------------ # +# Run app when the container launches # +# ------------------------------------------------------------------------------------------------ # +WORKDIR /appfolder/app +# CMD julia --project -t auto main.jl +# CMD ["caddy", "run", "--config", "/appfolder/mountvolume/config/Caddyfile"] +# CMD ["nginx", "-g", "daemon off;"] +# CMD [ "sleep", "infinity" ] + +CMD ["/appfolder/mountvolume/llama.cpp/build/bin/llama-server", "-m", "/appfolder/mountvolume/Qwen3-Embedding-0.6B-Q8_0.gguf", "--host", "0.0.0.0", "--port", "8080", "--embeddings", "-ngl", "0"] + + + + + + diff --git a/app/env_preparation.jl b/app/env_preparation.jl new file mode 100755 index 0000000..de799a1 --- /dev/null +++ b/app/env_preparation.jl @@ -0,0 +1,280 @@ +""" +For docker container: + - This script must be placed at the root of your project e.g. /appfolder/app + - This script expects all other files are in workfolder/temp e.g. /appfolder/app/temp +""" + +using Pkg + +programName = "someapp" # use my project name here +devPackageName = "somepackage" +appfolder = pwd() # use current folder as project folder + + +# ---------------------------------------------- 100 --------------------------------------------- # + +""" list of all recommend package +required_packages = [ + "Arrow" + "BenchmarkTools" + "CSV" + "CUDA" + "ClickHouse" + "DataFrames" + "DataFramesMeta" + "DataStructures" + "Distributions" + "Enzyme" + "FileIO" + "Flux" + "Genie" + "HTTP" + "ImageTransformations" + "Images" + "IterTools" + "JSON3" + "LinearAlgebra" + "Logging" + "Lux" + "MLDataUtils" + "MLDatasets" + "MLLabelUtils" + "Makie" + "ODBC" + "Optimisers" + "ParameterSchedulers" + "PkgTemplates" + "ProgressMeter" + "PyCall" + "Random" + "Revise" + "Serialization" + "Statistics" + "Stipple" + "TensorBoardLogger" + "TextAnalysis" + "Transformers" + "UUIDs" + "WordTokenizers" + "Zygote" + "https://github.com/denglerchr/Mosquitto.jl" +] +""" + + +# ---------------------------------------------------------------------------- # +# add julia packages in container's general registry folder # +# ---------------------------------------------------------------------------- # +privatejuliapkgpath = "$appfolder/dev" +required_julia_private_packages = [ #CHANGE + # "https://git.yiem.cc/ton/GeneralUtils", + # "https://git.yiem.cc/ton/LLMMCTS", + # "https://git.yiem.cc/ton/SQLLLM", + # "https://git.yiem.cc/ton/YiemAgent -b v0.1.1", +] + + +# ---------------------------------------------------------------------------- # +# add julia package in app folder # +# ---------------------------------------------------------------------------- # +required_julia_packages = [ #CHANGE + # "Revise", + # "CondaPkg", + # "PythonCall", + # "HTTP", + # "Oxygen", + # "JSON", + # "Dates", + # "UUIDs", + # "Random", + # "URIs", + # "DataStructures", + # "FileIO", + # "PrettyPrinting", + # "ZipArchives", + # "LibPQ", + # "ProgressMeter", + # "NATS" +] # use only for new project # use only for new project + +function copy_folder_contents(src_folder::AbstractString, dst_folder::AbstractString) + # Get a list of all files in the source folder + files = readdir(src_folder) + + # Iterate over each file and copy it to the destination folder + for itemname in files + src_path = joinpath(src_folder, itemname) + dst_path = joinpath(dst_folder, itemname) + if isdir(src_path) + run(`cp -r $src_path $dst_path`) + else + run(`cp $src_path $dst_path`) + end + end +end + +function install_required_julia_packages(required_julia_packages) + for i in required_julia_packages + println("adding ", i) + if startswith(i, "http") + Pkg.add(url=i) + else + Pkg.add(i) + end + end +end + +function install_required_julia_private_packages(required_julia_private_packages::Vector, + appfolderpath::String, privatejuliapkgpath::String) + + !isdir(privatejuliapkgpath) ? mkpath(privatejuliapkgpath) : nothing + cd(privatejuliapkgpath) + + if length(required_julia_private_packages) > 0 + # remove private packages already in Project.toml in case private package path is different + for i in required_julia_private_packages + _pkgname = split(i, "/")[end] + _pkgname = split(_pkgname, " ")[1] + pkgname = split(_pkgname, ".")[1] + println("removing Julia private package: $pkgname if it already in Project.toml") + try Pkg.rm(pkgname) catch end + end + + # clone all required julia private packages + for i in required_julia_private_packages + _pkgname = split(i, "/")[end] + _pkgname = split(_pkgname, " ")[1] + pkgname = split(_pkgname, ".")[1] + pkgpath = joinpath(privatejuliapkgpath, pkgname) + println("cloning Julia private package: $pkgname") + + # only clone a package if it is not found in local + gitcommand = "git clone $i" + args = split(gitcommand, " ") + !isdir(pkgpath) ? run(`$args`) : nothing + end + + # install all required julia private packages + for i in required_julia_private_packages + _pkgname = split(i, "/")[end] + _pkgname = split(_pkgname, " ")[1] + pkgname = split(_pkgname, ".")[1] # in case there is a .jl at the end of package name + pkgpath = joinpath(privatejuliapkgpath, pkgname) + println("installing Julia private package: $pkgname") + + Pkg.develop(path=pkgpath) + end + end + + cd(appfolderpath) +end + + +# ------------------------------------------------------------------------------------------------ # +# start script # +# ------------------------------------------------------------------------------------------------ # + +# all files are put in ./temp (most likely in a docker container) +if isdir(joinpath(appfolder, "temp")) + if isfile(joinpath(appfolder, "temp", "Project.toml")) # use this option if one already have project.toml + copy_folder_contents(joinpath(appfolder, "temp"), appfolder) + Pkg.activate(".") + install_required_julia_private_packages(required_julia_private_packages, appfolder, privatejuliapkgpath) + Pkg.instantiate() + else # no Project.toml, create new package + copy_folder_contents(joinpath(appfolder, "temp"), appfolder) + Pkg.activate(".") + length(required_julia_packages) != 0 ? install_required_julia_packages(required_julia_packages) : nothing + + # for app + write("main.jl", "using Revise # remove when this package is completed\n", "using $devPackageName\n", "# ---------------------------------------------- 100 --------------------------------------------- #\n") + mkdir("testapp") + cd("testapp") + write("runtests.jl", "# ---------------------------------------------- 100 --------------------------------------------- #\n") + cd(appfolder) + install_required_julia_private_packages(required_julia_private_packages, appfolder, privatejuliapkgpath) + + # create new dev package folder + !isdir(privatejuliapkgpath) ? mkpath(privatejuliapkgpath) : nothing + cd(privatejuliapkgpath) + Pkg.generate(devPackageName) + newPackagePath = joinpath(privatejuliapkgpath, devPackageName) + Pkg.develop(path=newPackagePath) + cd(newPackagePath) + mkdir("test") + cd("./test") + write("runtests.jl", "using $devPackageName\n", "# ---------------------------------------------- 100 --------------------------------------------- #\n") + cd(appfolder) + end +else # all files are in the current work folder + if isfile(joinpath(appfolder, "Project.toml")) # use this option if one already have project.toml + Pkg.activate(".") + install_required_julia_private_packages(required_julia_private_packages, appfolder, privatejuliapkgpath) + Pkg.instantiate() + else # create new package folder + Pkg.activate(".") + length(required_julia_packages) != 0 ? install_required_julia_packages(required_julia_packages) : nothing + + # for app + write("main.jl", "using Revise # remove when this package is completed\n", "using $devPackageName\n", "# ---------------------------------------------- 100 --------------------------------------------- #\n") + mkdir("testapp") + cd("testapp") + write("runtests.jl", "# ---------------------------------------------- 100 --------------------------------------------- #\n") + cd(appfolder) + install_required_julia_private_packages(required_julia_private_packages, appfolder, privatejuliapkgpath) + + # create new dev package folder + !isdir(privatejuliapkgpath) ? mkpath(privatejuliapkgpath) : nothing + cd(privatejuliapkgpath) + Pkg.generate(devPackageName) + newPackagePath = joinpath(privatejuliapkgpath, devPackageName) + Pkg.develop(path=newPackagePath) + cd(newPackagePath) + mkdir("test") + cd("./test") + write("runtests.jl", "using $devPackageName\n", "# ---------------------------------------------- 100 --------------------------------------------- #\n") + cd(appfolder) + + # preparing typical julia project finished now doing project's specific preparation here + end +end + + +# ---------------------------------------------------------------------------- # +# python libraries used by Julia's PythonCall # +# ---------------------------------------------------------------------------- # +pythonlib = [] +channels = ["anaconda", "conda-forge", "pytorch"] +if length(pythonlib) != 0 + using CondaPkg; + for i in channels CondaPkg.add_channel(i) end + for i in pythonlib CondaPkg.add_pip(i) end +end + +# ---------------------------------------------------------------------------- # +# create symlink # +# ---------------------------------------------------------------------------- # +# if isfile("/app/run.jl") +# symlink("/app/$programName/run.jl", "/app/run.jl") +# elseif isfile("/app/$programName/main.py") +# symlink("/app/$programName/main.py", "/app/main.py") +# else +# cd("/app") +# write("no run.jl in package folder.txt", "") +# end + + +# ------------------------------------------------------------------------------------------------ # +# for web app # +# ------------------------------------------------------------------------------------------------ # +# intendedURL = "wine.yiem.cc/hq/agent/sommelier/frontend/dbadmin" # e.g. "wine.yiem.cc/hq/agent/sommelier/frontend/dbadmin" +# urlList = split(intendedURL, "/")[2:end] # e.g. ["hq", "agent", "sommelier", "frontend", "dbadmin"] +# # use joinpath to compose the path from urlList +# _path = joinpath(urlList...) +# path = "./$_path" +# mkpath(path) +# # move all files in the current folder to the path +# copy_folder_contents(joinpath(appfolder, "temp"), path) + + +println("--> Julia env preparation done") \ No newline at end of file diff --git a/app/required_python_packages.txt b/app/required_python_packages.txt new file mode 100755 index 0000000..39d3dfa --- /dev/null +++ b/app/required_python_packages.txt @@ -0,0 +1,2 @@ +# Base ---------------------------------------- +python-dev-tools \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100755 index 0000000..dc8089d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,71 @@ + + +# You should know that Docker Compose works with services. +# 1 service = 1 container. +# For example, a service, a server, a client, a database... +# We use the keyword 'services' to start to create services. +services: + app: # service name + container_name: llamacpp_embedding_instance_1 + build: ./app + image: llamacpp_embedding_instance_1 + restart: always + environment: + - TZ=Asia/Bangkok + + - MODELFILENAME=Qwen3-Embedding-0.6B-Q8_0.gguf + - SERVERARGS=--host 0.0.0.0 --port 8080 --embeddings -ngl 0 + # network_mode: "app-network" + # networks: + # - default + ports: + - 9010:8080 + volumes: + - ./mountvolume:/appfolder/mountvolume + # privileged: true + deploy: + resources: + limits: + cpus: '10' + # # memory: 7200M + # reservations: + # devices: + # - driver: nvidia + # device_ids: ['3'] # choose between device_ids or count + # capabilities: [gpu] + + # The name of our service is "database" + # but you can use the name of your choice. + # Note: This may change the commands you are going to use a little bit. + # database: + # # Official Postgres image from DockerHub (we use the last version) + # image: 'postgres:latest' + + # By default, a Postgres database is running on the 5432 port. + # If we want to access the database from our computer (outside the container), + # we must share the port with our computer's port. + # The syntax is [port we want on our machine]:[port we want to retrieve in the container] + # Note: You are free to change your computer's port, + # but take into consideration that it will change the way + # you are connecting to your database. + # networks: + # - backend + # ports: + # - 5432:5432 + + # environment: + # POSTGRES_USER: username # The PostgreSQL user (useful to connect to the database) + # POSTGRES_PASSWORD: password # The PostgreSQL password (useful to connect to the database) + + # volumes: + # # In this example, we share the folder `db-data` in our root repository, with the default PostgreSQL data path. + # # It means that every time the repository is modifying the data inside + # # of `/var/lib/postgresql/data/`, automatically the change will appear in `db-data`. + # # You don't need to create the `db-data` folder. Docker Compose will do it for you. + # - ./mountvolume/dbdata/:/var/lib/postgresql/data/ + +# networks: +# default: + # name: https-network + # driver: bridge + # external: true