diff --git a/entitleme.plist b/entitleme.plist
new file mode 100644
index 0000000..60c9cb3
--- /dev/null
+++ b/entitleme.plist
@@ -0,0 +1,9 @@
+
+
+
+ com.apple.security.cs.allow-jit
+
+
+
+
diff --git a/pkg/build.sh b/pkg/build.sh
index e9375fa..c39f4dc 100755
--- a/pkg/build.sh
+++ b/pkg/build.sh
@@ -43,6 +43,31 @@ codesign --force \
--strict \
"${PKG_CLI_BIN_PATH}/tiles"
+
+echo "Embedding Pi"
+# Copying pi artifacts into extracted pi folder
+cp pi-darwin-arm64.tar.gz "${PKG_LIBS_PATH}"
+
+tar -xvf "${PKG_LIBS_PATH}/pi-darwin-arm64.tar.gz" -C "${PKG_LIBS_PATH}"
+
+rm "${PKG_LIBS_PATH}/pi-darwin-arm64.tar.gz"
+
+# removing unnecessary files
+# rm -rf "${DIST_DIR}/tmp/pi/examples"
+
+# Signing the pi binary
+
+echo "Signing Pi binary..."
+
+codesign --force \
+ --sign "$DEVELOPER_ID_APPLICATION" \
+ --options runtime \
+ --timestamp \
+ --entitlements entitleme.plist \
+ --strict \
+ "${PKG_LIBS_PATH}/pi/pi"
+
+
# Build venvstack and move to /usr/local/share/tiles
#
# flushing this folder, else the final zip will have previous app-server zips too (#84)
diff --git a/pkg/bundle_network_installer.sh b/pkg/bundle_network_installer.sh
index f6e8d45..b239087 100755
--- a/pkg/bundle_network_installer.sh
+++ b/pkg/bundle_network_installer.sh
@@ -14,6 +14,7 @@ productbuild \
# signing
productsign \
--sign "$DEVELOPER_ID_INSTALLER" \
+ --entitlements entitleme.plist \
pkg/tiles-dist-unsigned.pkg \
pkg/tiles.pkg
diff --git a/scripts/bundler.sh b/scripts/bundler.sh
index 8b962b8..c57a23b 100755
--- a/scripts/bundler.sh
+++ b/scripts/bundler.sh
@@ -16,7 +16,8 @@ VERSION=$(grep '^version' tiles/Cargo.toml | head -1 | awk -F'"' '{print $2}')
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
-# Final tar.gz name
+# Name for final tar.gz
+
OUT_NAME="${BINARY_NAME}-v${VERSION}-${ARCH}-${OS}"
echo "🚀 Building ${BINARY_NAME} (${TARGET} mode)..."
@@ -24,27 +25,20 @@ echo "🚀 Building ${BINARY_NAME} (${TARGET} mode)..."
cargo build -p tiles --${TARGET}
-# Destination where the release build is generated
+# Destination where the release binary is generated
CLI_BIN_PATH="target/${TARGET}/${BINARY_NAME}"
chmod +x "${CLI_BIN_PATH}"
-# echo "Signing the Tiles binary..."
-
-# # Signing the tiles binary
-# codesign --force \
-# --sign "$DEVELOPER_ID_APPLICATION"\
-# --options runtime \
-# --timestamp \
-# --strict \
-# "${CLI_BIN_PATH}"
+echo "Signing the Tiles binary..."
-# # echo "Notarizing Tiles binary..."
-
-# # notarizing the tiles binary
-# xcrun notarytool submit --force "${CLI_BIN_PATH}" \
-# --keychain-profile "tiles-notary-profile" \
-# --wait
+# Signing the tiles binary
+codesign --force \
+ --sign "$DEVELOPER_ID_APPLICATION"\
+ --options runtime \
+ --timestamp \
+ --strict \
+ "${CLI_BIN_PATH}"
mkdir -p "${DIST_DIR}/tmp"
@@ -60,43 +54,41 @@ tar -xvf "${DIST_DIR}/tmp/pi-darwin-arm64.tar.gz" -C "${DIST_DIR}/tmp"
rm "${DIST_DIR}/tmp/pi-darwin-arm64.tar.gz"
# removing unnecessary files
-rm -rf "${DIST_DIR}/tmp/pi/examples"
+# rm -rf "${DIST_DIR}/tmp/pi/examples"
# Signing the pi binary
-# codesign --force \
-# --sign "$DEVELOPER_ID_APPLICATION"\
-# --options runtime \
-# --timestamp \
-# --strict \
-# "${DIST_DIR}/tmp/pi/pi"
-# echo "Notarizing Pibinary..."
+echo "Signing Pi binary..."
+
+codesign --force \
+ --sign "$DEVELOPER_ID_APPLICATION" \
+ --options runtime \
+ --timestamp \
+ --entitlements entitleme.plist \
+ --strict \
+ "${DIST_DIR}/tmp/pi/pi"
-# # notarizing the pi binary
-# xcrun notarytool submit --force "${DIST_DIR}/tmp/pi/pi" \
-# --keychain-profile "tiles-notary-profile" \
-# --wait
# flushing this folder, else the final zip will have previous app-server zips too (#84)
-# rm -rf "${SERVER_DIR}/stack_export_prod"
+rm -rf "${SERVER_DIR}/stack_export_prod"
-# echo "🔒 Locking the venvstack...."
+echo "🔒 Locking the venvstack...."
-# venvstacks lock server/stack/venvstacks.toml
+venvstacks lock server/stack/venvstacks.toml
-# echo "🛠️ Building the venvstack...."
+echo "🛠️ Building the venvstack...."
-# venvstacks build server/stack/venvstacks.toml
+venvstacks build server/stack/venvstacks.toml
-# echo "📦 Publishing the venvstack...."
+echo "📦 Publishing the venvstack...."
-# venvstacks publish --tag-outputs --output-dir ../stack_export_prod server/stack/venvstacks.toml
+venvstacks publish --tag-outputs --output-dir ../stack_export_prod server/stack/venvstacks.toml
cp -r "${SERVER_DIR}" "${DIST_DIR}/tmp/"
rm -rf "${DIST_DIR}/tmp/server/__pycache__"
-rm -rf "${DIST_DIR}/tmp/server/mem_agent/__pycache__"
-rm -rf "${DIST_DIR}/tmp/server/backend/__pycache__"
+# rm -rf "${DIST_DIR}/tmp/server/mem_agent/__pycache__"
+# rm -rf "${DIST_DIR}/tmp/server/backend/__pycache__"
rm -rf "${DIST_DIR}/tmp/server/.venv"
rm -rf "${DIST_DIR}/tmp/server/stack"
diff --git a/scripts/install.sh b/scripts/install.sh
index 1dbcae7..e6de591 100755
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -14,6 +14,7 @@ MODELFILE_DIR="/usr/local/share/tiles/modelfiles" # Modelfile server folder
PI_DIR="/usr/local/share/tiles/pi"
TMPDIR="$(mktemp -d)"
+
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
@@ -57,11 +58,8 @@ rm -rf "${PI_DIR}"
mkdir -p "${PI_DIR}"
-cp -r "${TMPDIR}/pi-darwin-arm64.tar.gz" "${PI_DIR}/"
-
-tar -xvf "${PI_DIR}/pi-darwin-arm64.tar.gz"
+cp -r "${TMPDIR}/pi"/* "${PI_DIR}/"
-rm "${PI_DIR}/pi-darwin-arm64.tar.gz"
log "📦 Installing Python server to ${SERVER_DIR}..."
rm -rf "${SERVER_DIR}"
diff --git a/scripts/tar.exclude b/scripts/tar.exclude
index 02539a9..bd2e0a1 100644
--- a/scripts/tar.exclude
+++ b/scripts/tar.exclude
@@ -5,4 +5,3 @@ __pycache__
.env
.git
.DS_Store
-
diff --git a/server/api.py b/server/api.py
index e55cc54..d0b13e1 100644
--- a/server/api.py
+++ b/server/api.py
@@ -34,7 +34,7 @@ app = FastAPI()
@app.get("/ping")
async def ping():
- return {"message": "Badda-Bing Badda-Bang"}
+ return {"message": "Welcome to the jungle"}
@app.post("/start")
@@ -78,20 +78,36 @@ async def create_chat_completion(request: ChatCompletionRequest):
raise HTTPException(status_code=500, detail=str(e))
-@app.exception_handler(RequestValidationError)
-async def validation_exception_handler(request: Request, exc: RequestValidationError):
+@app.exception_handler(HTTPException)
+async def validation_exception_handler(request: Request, exc: HTTPException):
return JSONResponse(
- status_code=422,
- content={"detail": exc.errors()},
+ status_code=exc.status_code,
+ content={"detail": exc.detail},
)
+@app.middleware("http")
+async def catch_all(request, call_next):
+ try:
+ return await call_next(request)
+ except Exception as e:
+ print("UNCAUGHT:", repr(e))
+ raise
+
+
@app.post("/v1/responses")
async def create_chat_response(request: ResponsesRequest):
"""
Create a response with openResponses format
"""
+ try:
+ ResponsesRequest.model_validate(request)
+ except Exception as e:
+ print(e)
+
+ print(f"REQUEST => {request}")
+
if request.stream:
return StreamingResponse(
runtime.backend.generate_response_chat_stream(request),
diff --git a/server/backend/mlx.py b/server/backend/mlx.py
index d6c3395..5899096 100644
--- a/server/backend/mlx.py
+++ b/server/backend/mlx.py
@@ -29,6 +29,10 @@ from ..reasoning_utils import ReasoningExtractor
from ..cache_utils import get_model_path
from ..schemas import (
+ CAssistantMessageItemParam,
+ CDeveloperMessageItemParam,
+ CSystemMessageItemParam,
+ CUserMessageItemParam,
ChatCompletionRequest,
ChatMessage,
GenerationMetrics,
@@ -721,7 +725,7 @@ def build_harmony_conversation(
for item in convos:
print(f"ITEM {item}")
match item:
- case UserMessageItemParam():
+ case CUserMessageItemParam():
content = ""
if isinstance(item.content, list):
content = item.content[0].text
@@ -730,7 +734,7 @@ def build_harmony_conversation(
convo_list.append(
Message.from_role_and_content(Role.USER, content) # pyright: ignore
)
- case DeveloperMessageItemParam():
+ case CDeveloperMessageItemParam():
convo_list.append(
Message.from_role_and_content(
Role.DEVELOPER,
@@ -739,13 +743,13 @@ def build_harmony_conversation(
), # pyright: ignore )
)
)
- case AssistantMessageItemParam():
+ case CAssistantMessageItemParam():
convo_list.append(
Message.from_role_and_content(
Role.ASSISTANT, item.content.root
) # pyright: ignore
)
- case SystemMessageItemParam():
+ case CSystemMessageItemParam():
convo_list.append(
Message.from_role_and_content(Role.SYSTEM, item.content.root)
)
diff --git a/server/pyproject.toml b/server/pyproject.toml
index 3ad4e4f..62dc5d4 100644
--- a/server/pyproject.toml
+++ b/server/pyproject.toml
@@ -21,4 +21,4 @@ build-backend = "setuptools.build_meta"
exclude = ["backend", "backend.*"]
[tool.uv]
-exclude-newer="10 days"
+exclude-newer="2026-04-05T09:43:13.300458+00:00"
diff --git a/server/schemas.py b/server/schemas.py
index 0c1c131..771a681 100644
--- a/server/schemas.py
+++ b/server/schemas.py
@@ -1,6 +1,6 @@
from dataclasses import dataclass
from enum import Enum, auto
-from typing import Any, Dict, List, Union, override
+from typing import Any, Dict, List, Literal, Union, override
from openresponses_types import ReasoningParam, TruncationEnum
from openresponses_types.types import (
@@ -88,6 +88,30 @@ class downloadRequest(BaseModel):
model: str
+class CUserMessageItemParam(UserMessageItemParam):
+ type: Literal["message"] = "message"
+
+
+class CSystemMessageItemParam(SystemMessageItemParam):
+ type: Literal["message"] = "message"
+
+
+class CDeveloperMessageItemParam(DeveloperMessageItemParam):
+ type: Literal["message"] = "message"
+
+
+class CAssistantMessageItemParam(AssistantMessageItemParam):
+ type: Literal["message"] = "message"
+
+
+class CFunctionCallItemParam(FunctionCallItemParam):
+ type: Literal["function_call"] = "function_call"
+
+
+class CFunctionCallOutputItemParam(FunctionCallOutputItemParam):
+ type: Literal["function_call_output"] = "function_call_output"
+
+
class ResponsesRequest(BaseModel):
model: str = "mlx-community/gpt-oss-20b-MXFP4-Q4"
input: (
@@ -95,12 +119,12 @@ class ResponsesRequest(BaseModel):
| list[
ItemReferenceParam
| ReasoningItemParam
- | UserMessageItemParam
- | SystemMessageItemParam
- | DeveloperMessageItemParam
- | AssistantMessageItemParam
- | FunctionCallItemParam
- | FunctionCallOutputItemParam
+ | CUserMessageItemParam
+ | CSystemMessageItemParam
+ | CDeveloperMessageItemParam
+ | CAssistantMessageItemParam
+ | CFunctionCallItemParam
+ | CFunctionCallOutputItemParam
]
)
reasoning: ReasoningParam = ReasoningParam(
diff --git a/server/stack/requirements/app-server/pylock.app-server.meta.json b/server/stack/requirements/app-server/pylock.app-server.meta.json
index cfa1b88..25fd480 100644
--- a/server/stack/requirements/app-server/pylock.app-server.meta.json
+++ b/server/stack/requirements/app-server/pylock.app-server.meta.json
@@ -1,5 +1,5 @@
{
- "lock_input_hash": "sha256:d7a2f2a68301eb65d01fc121715ef8b96a6af48a315d580340d976fde6e1dc47",
+ "lock_input_hash": "sha256:3c4cafa1e5147c3de588c39652d8ba0046be8d1bea5ae8a547a7bb8b1bb9e734",
"lock_version": 1,
"locked_at": "2026-04-05T09:43:13.300458+00:00",
"other_inputs_hash": "sha256:63b3c2cfe2ec414938e81dace7aac779c7b902bae681618cd8827e9f16880985",
diff --git a/server/stack/requirements/app-server/requirements-app-server.in b/server/stack/requirements/app-server/requirements-app-server.in
index 346cc5f..8bfa8b9 100644
--- a/server/stack/requirements/app-server/requirements-app-server.in
+++ b/server/stack/requirements/app-server/requirements-app-server.in
@@ -6,3 +6,4 @@ mlx-lm==0.31.0
black==25.9.0
openai-harmony==0.0.8
openresponses-types
+httpx==0.28.1
diff --git a/server/stack/venvstacks.toml b/server/stack/venvstacks.toml
index db30051..6d67ecb 100644
--- a/server/stack/venvstacks.toml
+++ b/server/stack/venvstacks.toml
@@ -27,10 +27,13 @@ requirements = [
"mlx-lm==0.31.0",
"black==25.9.0",
"openai-harmony==0.0.8",
- "openresponses-types"
+ "openresponses-types",
+ "httpx==0.28.1"
]
[tool.uv]
+exclude-newer="2026-04-05T09:43:13.300458+00:00"
+
# Only resolve for the relevant target platforms
environments = [
"sys_platform == 'darwin' and platform_machine == 'arm64'",
diff --git a/tiles/src/core/chats.rs b/tiles/src/core/chats.rs
index a1927cd..adcd7e9 100644
--- a/tiles/src/core/chats.rs
+++ b/tiles/src/core/chats.rs
@@ -154,7 +154,7 @@ pub fn get_delta(conn: &Connection, user_id: &str, last_row_couter: i64) -> Resu
let session_id: String = session_id_db.unwrap_or("".to_owned());
- if session_id.len() > 0 && !session_map.contains_key(&session_id) {
+ if !session_id.is_empty() && !session_map.contains_key(&session_id) {
// lets fetch the session details
match fetch_session(conn, &session_id) {
Ok(session) => {
@@ -187,10 +187,7 @@ pub fn get_delta(conn: &Connection, user_id: &str, last_row_couter: i64) -> Resu
let sessions: Vec = session_map.into_values().collect();
- Ok(DeltaChat {
- chats: chats,
- sessions: sessions,
- })
+ Ok(DeltaChat { chats, sessions })
}
pub fn apply_delta(chat_conn: &mut Connection, delta_chats: DeltaChat) -> Result<()> {
diff --git a/tiles/src/runtime/mlx.rs b/tiles/src/runtime/mlx.rs
index a9b0795..3abe0e1 100644
--- a/tiles/src/runtime/mlx.rs
+++ b/tiles/src/runtime/mlx.rs
@@ -1,4 +1,4 @@
-use crate::core::accounts::{User, get_current_user};
+use crate::core::accounts::get_current_user;
use crate::core::chats::{Message, create_session, save_chat};
use crate::core::storage::db::Dbconn;
use crate::runtime::RunArgs;
@@ -37,6 +37,7 @@ pub struct BenchmarkMetrics {
total_latency_s: f64,
}
+#[allow(dead_code)]
impl BenchmarkMetrics {
fn update(&mut self, metrics: BenchmarkMetrics) -> &Self {
if self.ttft_ms == 0.0 {
@@ -323,7 +324,7 @@ async fn run_model_with_server(
async fn start_repl(
mlx_runtime: &MLXRuntime,
modelfile: &Modelfile,
- run_args: &RunArgs,
+ _run_args: &RunArgs,
db_conn: &Dbconn,
) -> Result<()> {
let modelname = modelfile
@@ -337,10 +338,10 @@ async fn start_repl(
let config = Config::builder().auto_add_history(true).build();
let mut editor = Editor::::with_config(config).unwrap();
editor.set_helper(Some(TilesHinter));
- let mut g_reply: String = "".to_owned();
- let mut prev_response_id: String = String::from("");
+ // let mut g_reply: String = "".to_owned();
+ // let mut prev_response_id: String = String::from("");
- let mut conversations: Vec = vec![];
+ // let mut conversations: Vec = vec![];
let mut pi_process = start_pi_rpc(&modelname)?;
let mut session_id = String::new();
@@ -361,7 +362,6 @@ async fn start_repl(
let state: GetStateData =
serde_json::from_value(msg.data.expect("get state parsing failed"))?;
session_id = state.session_id;
- info!("Current session: {}", session_id);
}
loop {
@@ -432,15 +432,12 @@ async fn start_repl(
}
}
- let mut remaining_count = run_args.relay_count;
- let mut python_code: String = "".to_owned();
- let mut bench_metrics: BenchmarkMetrics = BenchmarkMetrics {
- ttft_ms: 0.0,
- total_tokens: 0,
- tokens_per_second: 0.0,
- total_latency_s: 0.0,
- };
- let mut is_agent_streaming: bool = false;
+ // let mut bench_metrics: BenchmarkMetrics = BenchmarkMetrics {
+ // ttft_ms: 0.0,
+ // total_tokens: 0,
+ // tokens_per_second: 0.0,
+ // total_latency_s: 0.0,
+ // };
let reader = BufReader::new(&mut stdout);
let mut session_turn_count = 0;
let mut last_chat_id: String = "".to_owned();
@@ -472,7 +469,7 @@ async fn start_repl(
// first time
if session_turn_count == 1 {
info!("Created session {}", session_id);
- create_session(&db_conn.chat, &session_id, "dummy", ¤t_user.user_id)?;
+ create_session(&db_conn.chat, &session_id, &input, ¤t_user.user_id)?;
}
let parent_chat_id = if session_turn_count == 1 {
None
@@ -513,7 +510,6 @@ async fn start_repl(
if response_msg.success {
match response_msg.command {
CommandType::Unknown => {
- println!("{}", line);
continue;
}
cmd => process_command(cmd, response_msg.data)?,
@@ -819,6 +815,7 @@ async fn load_model(
// }
// }
+#[allow(dead_code)]
fn extract_python(content: &str) -> String {
if content.contains("") && content.contains("") {
let list_a = content.split("").collect::>();
@@ -853,6 +850,8 @@ fn get_default_modelfile(memory_mode: bool) -> Result {
}
}
+//TODO: Deprecated if not needed
+#[allow(dead_code)]
fn create_chat_input(input: &str, prompt: &str, conversations: &[Message]) -> Vec {
let dev_msg = Message {
r#type: "message".to_owned(),
diff --git a/tiles/src/utils/config.rs b/tiles/src/utils/config.rs
index 6030c3d..5887b07 100644
--- a/tiles/src/utils/config.rs
+++ b/tiles/src/utils/config.rs
@@ -443,7 +443,7 @@ pub fn create_pi_provider_config(model_name: &str, enpoint_base_url: &str) -> Re
#[allow(dead_code)]
fn try_update_pi_provider_model(config: &str, model_name: &str) -> Result {
- let mut pi_model_config: PiModelConfig = serde_json::from_str(&config)?;
+ let mut pi_model_config: PiModelConfig = serde_json::from_str(config)?;
let mut tiles_provider_config: PiProviderConfig = pi_model_config
.providers
.get("tiles")