@rethinkingstudio/clawpilot 2.1.21-beta.4 → 2.1.21-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rethinkingstudio/clawpilot",
3
- "version": "2.1.21-beta.4",
3
+ "version": "2.1.21-beta.5",
4
4
  "description": "ClawAI relay client for Mac mini",
5
5
  "type": "module",
6
6
  "bin": {
@@ -87,30 +87,102 @@ def read_base_config_for_profile() -> Dict[str, Any]:
87
87
  return read_yaml_config(parent.parent / "config.yaml")
88
88
 
89
89
 
90
+ def normalize_message_content(value: Any) -> Any:
91
+ if value is None:
92
+ return ""
93
+ if isinstance(value, str):
94
+ return value
95
+ if isinstance(value, list):
96
+ parts: List[Any] = []
97
+ for part in value:
98
+ if isinstance(part, str):
99
+ parts.append({"type": "text", "text": part})
100
+ continue
101
+ if not isinstance(part, dict):
102
+ continue
103
+ clean_part = {k: v for k, v in part.items() if v is not None}
104
+ if clean_part.get("type") == "text":
105
+ clean_part["text"] = str(clean_part.get("text") or "")
106
+ if clean_part:
107
+ parts.append(clean_part)
108
+ return parts if parts else ""
109
+ return str(value)
110
+
111
+
112
+ def normalize_tool_calls(value: Any) -> List[Dict[str, Any]]:
113
+ if not isinstance(value, list):
114
+ return []
115
+ clean_calls: List[Dict[str, Any]] = []
116
+ for index, call in enumerate(value):
117
+ if not isinstance(call, dict):
118
+ continue
119
+ raw_function = call.get("function")
120
+ function = raw_function if isinstance(raw_function, dict) else {}
121
+ name = str(function.get("name") or call.get("name") or "").strip()
122
+ if not name:
123
+ continue
124
+ raw_arguments = function.get("arguments")
125
+ if raw_arguments is None:
126
+ arguments = "{}"
127
+ elif isinstance(raw_arguments, str):
128
+ arguments = raw_arguments
129
+ else:
130
+ try:
131
+ arguments = json.dumps(raw_arguments, ensure_ascii=False)
132
+ except Exception:
133
+ arguments = str(raw_arguments)
134
+ call_id = str(call.get("id") or call.get("call_id") or f"call_{index}").strip()
135
+ clean_calls.append(
136
+ {
137
+ "id": call_id,
138
+ "type": str(call.get("type") or "function"),
139
+ "function": {
140
+ "name": name,
141
+ "arguments": arguments,
142
+ },
143
+ }
144
+ )
145
+ return clean_calls
146
+
147
+
90
148
  def sanitize_messages_for_api(messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
91
149
  valid_tool_call_ids: set[str] = set()
92
150
  for msg in messages:
93
151
  if not isinstance(msg, dict):
94
152
  continue
95
153
  if msg.get("role") == "assistant":
96
- for tc in msg.get("tool_calls") or []:
97
- if isinstance(tc, dict):
98
- tid = tc.get("id") or tc.get("call_id") or ""
99
- if tid:
100
- valid_tool_call_ids.add(str(tid))
154
+ for tc in normalize_tool_calls(msg.get("tool_calls")):
155
+ tid = tc.get("id") or ""
156
+ if tid:
157
+ valid_tool_call_ids.add(str(tid))
101
158
 
102
159
  clean: List[Dict[str, Any]] = []
103
160
  for msg in messages:
104
161
  if not isinstance(msg, dict):
105
162
  continue
106
- role = msg.get("role")
163
+ role = str(msg.get("role") or "").strip()
164
+ if role not in {"system", "user", "assistant", "tool"}:
165
+ continue
166
+ sanitized: Dict[str, Any] = {"role": role}
107
167
  if role == "tool":
108
168
  tid = str(msg.get("tool_call_id") or "")
109
169
  if not tid or tid not in valid_tool_call_ids:
110
170
  continue
111
- sanitized = {k: v for k, v in msg.items() if k in _API_SAFE_MSG_KEYS}
112
- if sanitized.get("role"):
113
- clean.append(sanitized)
171
+ sanitized["tool_call_id"] = tid
172
+ if msg.get("name"):
173
+ sanitized["name"] = str(msg.get("name"))
174
+
175
+ if role == "assistant":
176
+ tool_calls = normalize_tool_calls(msg.get("tool_calls"))
177
+ if tool_calls:
178
+ sanitized["tool_calls"] = tool_calls
179
+
180
+ content = normalize_message_content(msg.get("content"))
181
+ if content or role != "assistant" or sanitized.get("tool_calls"):
182
+ sanitized["content"] = content
183
+ if msg.get("refusal") is not None:
184
+ sanitized["refusal"] = str(msg.get("refusal"))
185
+ clean.append(sanitized)
114
186
  return clean
115
187
 
116
188