megharudushi commited on
Commit
2d169db
·
verified ·
1 Parent(s): 1e22395

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +452 -5
app.py CHANGED
@@ -6,6 +6,7 @@ Author: Matrix Agent
6
  Features:
7
  - Full OpenAI API compatibility (/v1/chat/completions)
8
  - Full Anthropic API compatibility (/v1/messages)
 
9
  - Prefill Response Support (assistant message prefix for output control)
10
  - Thinking/Reasoning Content Block Support
11
  - Optimized for coding tasks
@@ -14,6 +15,7 @@ Features:
14
  API Specifications verified against:
15
  - OpenAI: https://platform.openai.com/docs/api-reference/chat/create
16
  - Anthropic: https://docs.anthropic.com/en/api/messages
 
17
  - Prefill: https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prefill-claudes-response
18
  - MiniMax Anthropic: https://platform.minimax.io/docs/api-reference/text-anthropic-api
19
  """
@@ -64,6 +66,9 @@ MODEL_ALIASES = {
64
  "claude-3-haiku": MODEL_ID,
65
  "claude-3-5-sonnet": MODEL_ID,
66
  "claude-code": MODEL_ID,
 
 
 
67
  }
68
 
69
  API_KEY = os.getenv("API_KEY", "sk-free-coding-api")
@@ -517,8 +522,8 @@ async def lifespan(app: FastAPI):
517
 
518
  app = FastAPI(
519
  title="Free Coding API",
520
- description="OpenAI & Anthropic compatible API with Prefill & Thinking support",
521
- version="1.1.0",
522
  lifespan=lifespan
523
  )
524
 
@@ -907,6 +912,443 @@ async def anthropic_messages(
907
  )
908
  )
909
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
910
  # ============================================================================
911
  # Health & Info Endpoints
912
  # ============================================================================
@@ -915,22 +1357,27 @@ async def anthropic_messages(
915
  async def root():
916
  return {
917
  "name": "Free Coding API",
918
- "version": "1.1.0",
919
  "model": MODEL_ID,
920
  "features": {
921
  "prefill_response": "Supported - Include assistant message at end for output control",
922
  "thinking": "Supported - Enable with thinking: {type: 'enabled'}",
923
- "streaming": "Supported - Both OpenAI and Anthropic formats"
 
924
  },
925
  "compatibility": {
926
  "openai": "v1 Chat Completions API",
927
- "anthropic": "Messages API (2023-06-01)"
 
928
  },
929
  "endpoints": {
930
  "openai_chat": "/v1/chat/completions",
931
  "anthropic_messages": "/v1/messages",
 
 
932
  "models": "/v1/models"
933
  },
 
934
  "docs": "/docs"
935
  }
936
 
 
6
  Features:
7
  - Full OpenAI API compatibility (/v1/chat/completions)
8
  - Full Anthropic API compatibility (/v1/messages)
9
+ - Computer Use Agent (CUA) endpoint (/v1/cua)
10
  - Prefill Response Support (assistant message prefix for output control)
11
  - Thinking/Reasoning Content Block Support
12
  - Optimized for coding tasks
 
15
  API Specifications verified against:
16
  - OpenAI: https://platform.openai.com/docs/api-reference/chat/create
17
  - Anthropic: https://docs.anthropic.com/en/api/messages
18
+ - Anthropic Computer Use: https://docs.anthropic.com/en/docs/agents-and-tools/computer-use
19
  - Prefill: https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prefill-claudes-response
20
  - MiniMax Anthropic: https://platform.minimax.io/docs/api-reference/text-anthropic-api
21
  """
 
66
  "claude-3-haiku": MODEL_ID,
67
  "claude-3-5-sonnet": MODEL_ID,
68
  "claude-code": MODEL_ID,
69
+ # Computer Use Agent (CUA) model
70
+ "sheikh-computer-use-preview": MODEL_ID,
71
+ "computer-use-preview": MODEL_ID,
72
  }
73
 
74
  API_KEY = os.getenv("API_KEY", "sk-free-coding-api")
 
522
 
523
  app = FastAPI(
524
  title="Free Coding API",
525
+ description="OpenAI & Anthropic compatible API with Prefill, Thinking & Computer Use Agent (CUA) support",
526
+ version="1.2.0",
527
  lifespan=lifespan
528
  )
529
 
 
912
  )
913
  )
914
 
915
+ # ============================================================================
916
+ # Computer Use Agent (CUA) - Pydantic Models
917
+ # ============================================================================
918
+
919
+ class CUAToolAction(BaseModel):
920
+ """Computer use tool action"""
921
+ type: str # "click", "type", "scroll", "screenshot", "key", "move", "drag", "wait"
922
+ # For click/move/drag
923
+ x: Optional[int] = None
924
+ y: Optional[int] = None
925
+ button: Optional[str] = "left" # "left", "right", "middle"
926
+ # For type
927
+ text: Optional[str] = None
928
+ # For key
929
+ key: Optional[str] = None # "enter", "tab", "escape", "backspace", etc.
930
+ modifiers: Optional[List[str]] = None # ["ctrl", "shift", "alt", "meta"]
931
+ # For scroll
932
+ direction: Optional[str] = None # "up", "down", "left", "right"
933
+ amount: Optional[int] = None # pixels or lines
934
+ # For drag
935
+ start_x: Optional[int] = None
936
+ start_y: Optional[int] = None
937
+ end_x: Optional[int] = None
938
+ end_y: Optional[int] = None
939
+ # For wait
940
+ duration: Optional[float] = None # seconds
941
+
942
+ class CUAToolResult(BaseModel):
943
+ """Result of a computer use tool action"""
944
+ type: str = "tool_result"
945
+ tool_use_id: str
946
+ content: Optional[Union[str, List[Dict]]] = None
947
+ is_error: Optional[bool] = False
948
+
949
+ class CUAScreenInfo(BaseModel):
950
+ """Screen configuration for CUA"""
951
+ width: int = 1920
952
+ height: int = 1080
953
+ display_number: Optional[int] = 0
954
+
955
+ class CUAComputerTool(BaseModel):
956
+ """Computer use tool definition"""
957
+ type: str = "computer_20241022"
958
+ name: str = "computer"
959
+ display_width_px: int = 1920
960
+ display_height_px: int = 1080
961
+ display_number: Optional[int] = 0
962
+
963
+ class CUAMessage(BaseModel):
964
+ """CUA message format"""
965
+ role: str
966
+ content: Union[str, List[Dict]]
967
+
968
+ class CUARequest(BaseModel):
969
+ """Computer Use Agent request"""
970
+ model: str = "sheikh-computer-use-preview"
971
+ messages: List[CUAMessage]
972
+ max_tokens: int = 4096
973
+ # Computer use specific
974
+ tools: Optional[List[Dict]] = None
975
+ tool_choice: Optional[Dict] = None
976
+ # Screen configuration
977
+ screen: Optional[CUAScreenInfo] = None
978
+ # Standard params
979
+ system: Optional[str] = None
980
+ temperature: Optional[float] = 0.7
981
+ stream: Optional[bool] = False
982
+ # Thinking mode
983
+ thinking: Optional[AnthropicThinkingConfig] = None
984
+
985
+ class CUAToolUseBlock(BaseModel):
986
+ """Tool use content block"""
987
+ type: str = "tool_use"
988
+ id: str
989
+ name: str
990
+ input: Dict
991
+
992
+ class CUAResponse(BaseModel):
993
+ """CUA response format"""
994
+ id: str
995
+ type: str = "message"
996
+ role: str = "assistant"
997
+ model: str
998
+ content: List[Dict]
999
+ stop_reason: Optional[str] = None
1000
+ usage: Dict
1001
+
1002
+ # ============================================================================
1003
+ # CUA - Computer Action Parser
1004
+ # ============================================================================
1005
+
1006
+ def parse_computer_action_from_text(text: str, screen_width: int = 1920, screen_height: int = 1080) -> Optional[Dict]:
1007
+ """
1008
+ Parse computer actions from model's text response.
1009
+ The model describes what actions it wants to take, and we parse them.
1010
+ """
1011
+ import re
1012
+
1013
+ text_lower = text.lower()
1014
+
1015
+ # Click patterns
1016
+ click_match = re.search(r'click\s+(?:at\s+)?(?:\()?(\d+)\s*[,\s]\s*(\d+)(?:\))?', text_lower)
1017
+ if click_match:
1018
+ return {
1019
+ "type": "tool_use",
1020
+ "id": f"toolu_{uuid.uuid4().hex[:24]}",
1021
+ "name": "computer",
1022
+ "input": {
1023
+ "action": "click",
1024
+ "coordinate": [int(click_match.group(1)), int(click_match.group(2))]
1025
+ }
1026
+ }
1027
+
1028
+ # Type patterns
1029
+ type_match = re.search(r'type\s+["\']([^"\']+)["\']', text, re.IGNORECASE)
1030
+ if type_match:
1031
+ return {
1032
+ "type": "tool_use",
1033
+ "id": f"toolu_{uuid.uuid4().hex[:24]}",
1034
+ "name": "computer",
1035
+ "input": {
1036
+ "action": "type",
1037
+ "text": type_match.group(1)
1038
+ }
1039
+ }
1040
+
1041
+ # Key press patterns
1042
+ key_match = re.search(r'press\s+(?:the\s+)?(\w+)\s+key', text_lower)
1043
+ if key_match:
1044
+ return {
1045
+ "type": "tool_use",
1046
+ "id": f"toolu_{uuid.uuid4().hex[:24]}",
1047
+ "name": "computer",
1048
+ "input": {
1049
+ "action": "key",
1050
+ "key": key_match.group(1)
1051
+ }
1052
+ }
1053
+
1054
+ # Screenshot request
1055
+ if 'screenshot' in text_lower or 'screen capture' in text_lower or 'take a picture' in text_lower:
1056
+ return {
1057
+ "type": "tool_use",
1058
+ "id": f"toolu_{uuid.uuid4().hex[:24]}",
1059
+ "name": "computer",
1060
+ "input": {
1061
+ "action": "screenshot"
1062
+ }
1063
+ }
1064
+
1065
+ # Scroll patterns
1066
+ scroll_match = re.search(r'scroll\s+(up|down|left|right)(?:\s+(\d+))?', text_lower)
1067
+ if scroll_match:
1068
+ return {
1069
+ "type": "tool_use",
1070
+ "id": f"toolu_{uuid.uuid4().hex[:24]}",
1071
+ "name": "computer",
1072
+ "input": {
1073
+ "action": "scroll",
1074
+ "coordinate": [screen_width // 2, screen_height // 2],
1075
+ "direction": scroll_match.group(1),
1076
+ "amount": int(scroll_match.group(2)) if scroll_match.group(2) else 3
1077
+ }
1078
+ }
1079
+
1080
+ # Move mouse
1081
+ move_match = re.search(r'move\s+(?:mouse\s+)?(?:to\s+)?(?:\()?(\d+)\s*[,\s]\s*(\d+)(?:\))?', text_lower)
1082
+ if move_match:
1083
+ return {
1084
+ "type": "tool_use",
1085
+ "id": f"toolu_{uuid.uuid4().hex[:24]}",
1086
+ "name": "computer",
1087
+ "input": {
1088
+ "action": "mouse_move",
1089
+ "coordinate": [int(move_match.group(1)), int(move_match.group(2))]
1090
+ }
1091
+ }
1092
+
1093
+ # Double click
1094
+ if 'double click' in text_lower or 'double-click' in text_lower:
1095
+ dbl_match = re.search(r'double[- ]click\s+(?:at\s+)?(?:\()?(\d+)\s*[,\s]\s*(\d+)(?:\))?', text_lower)
1096
+ if dbl_match:
1097
+ return {
1098
+ "type": "tool_use",
1099
+ "id": f"toolu_{uuid.uuid4().hex[:24]}",
1100
+ "name": "computer",
1101
+ "input": {
1102
+ "action": "double_click",
1103
+ "coordinate": [int(dbl_match.group(1)), int(dbl_match.group(2))]
1104
+ }
1105
+ }
1106
+
1107
+ # Right click
1108
+ if 'right click' in text_lower or 'right-click' in text_lower:
1109
+ right_match = re.search(r'right[- ]click\s+(?:at\s+)?(?:\()?(\d+)\s*[,\s]\s*(\d+)(?:\))?', text_lower)
1110
+ if right_match:
1111
+ return {
1112
+ "type": "tool_use",
1113
+ "id": f"toolu_{uuid.uuid4().hex[:24]}",
1114
+ "name": "computer",
1115
+ "input": {
1116
+ "action": "right_click",
1117
+ "coordinate": [int(right_match.group(1)), int(right_match.group(2))]
1118
+ }
1119
+ }
1120
+
1121
+ # Drag patterns
1122
+ drag_match = re.search(r'drag\s+from\s+(?:\()?(\d+)\s*[,\s]\s*(\d+)(?:\))?\s+to\s+(?:\()?(\d+)\s*[,\s]\s*(\d+)(?:\))?', text_lower)
1123
+ if drag_match:
1124
+ return {
1125
+ "type": "tool_use",
1126
+ "id": f"toolu_{uuid.uuid4().hex[:24]}",
1127
+ "name": "computer",
1128
+ "input": {
1129
+ "action": "left_click_drag",
1130
+ "start_coordinate": [int(drag_match.group(1)), int(drag_match.group(2))],
1131
+ "coordinate": [int(drag_match.group(3)), int(drag_match.group(4))]
1132
+ }
1133
+ }
1134
+
1135
+ return None
1136
+
1137
+ # ============================================================================
1138
+ # Computer Use Agent (CUA) Endpoint
1139
+ # ============================================================================
1140
+
1141
+ @app.post("/v1/cua")
1142
+ async def computer_use_agent(
1143
+ request: CUARequest,
1144
+ authorization: Optional[str] = Header(None),
1145
+ x_api_key: Optional[str] = Header(None, alias="x-api-key"),
1146
+ ):
1147
+ """
1148
+ Computer Use Agent endpoint - sheikh-computer-use-preview
1149
+
1150
+ This endpoint provides a computer control interface compatible with
1151
+ Anthropic's Computer Use API. It processes user requests and generates
1152
+ computer control actions (click, type, scroll, screenshot, etc.)
1153
+
1154
+ The model analyzes the request and current state (via screenshots) and
1155
+ outputs structured tool calls for computer control actions.
1156
+ """
1157
+
1158
+ auth_key = x_api_key or authorization
1159
+ if not verify_api_key(auth_key):
1160
+ raise HTTPException(status_code=401, detail="Invalid API key")
1161
+
1162
+ # Get screen configuration
1163
+ screen_width = 1920
1164
+ screen_height = 1080
1165
+ if request.screen:
1166
+ screen_width = request.screen.width
1167
+ screen_height = request.screen.height
1168
+
1169
+ # Build system prompt for computer use
1170
+ cua_system_prompt = f"""You are a Computer Use Agent (CUA) that helps users interact with computers.
1171
+ You can control the computer by describing actions you want to take.
1172
+
1173
+ Available actions:
1174
+ - click at (x, y) - Click at screen coordinates
1175
+ - double click at (x, y) - Double click at coordinates
1176
+ - right click at (x, y) - Right click at coordinates
1177
+ - type "text" - Type the specified text
1178
+ - press [key] key - Press a key (enter, tab, escape, backspace, etc.)
1179
+ - scroll [up/down/left/right] [amount] - Scroll the screen
1180
+ - move mouse to (x, y) - Move cursor to coordinates
1181
+ - drag from (x1, y1) to (x2, y2) - Drag from one point to another
1182
+ - screenshot - Request a screenshot of the current screen
1183
+
1184
+ Screen resolution: {screen_width}x{screen_height}
1185
+
1186
+ When analyzing a screenshot or user request, describe the actions needed step by step.
1187
+ Always specify exact coordinates when performing click or move actions.
1188
+ Be precise and methodical in your approach."""
1189
+
1190
+ if request.system:
1191
+ cua_system_prompt = request.system + "\n\n" + cua_system_prompt
1192
+
1193
+ # Extract messages
1194
+ messages = []
1195
+ for m in request.messages:
1196
+ content = m.content
1197
+ if isinstance(content, str):
1198
+ messages.append({"role": m.role, "content": content})
1199
+ elif isinstance(content, list):
1200
+ # Handle multimodal content (images, tool results)
1201
+ text_parts = []
1202
+ for block in content:
1203
+ if isinstance(block, dict):
1204
+ if block.get("type") == "text":
1205
+ text_parts.append(block.get("text", ""))
1206
+ elif block.get("type") == "image":
1207
+ text_parts.append("[Screenshot provided - analyzing...]")
1208
+ elif block.get("type") == "tool_result":
1209
+ text_parts.append(f"[Tool result: {block.get('content', '')}]")
1210
+ messages.append({"role": m.role, "content": "\n".join(text_parts)})
1211
+
1212
+ # Check for prefill
1213
+ messages, prefill = extract_prefill_from_messages(messages)
1214
+
1215
+ prompt = format_messages_for_model(messages, system_prompt=cua_system_prompt, prefill=prefill)
1216
+
1217
+ request_id = f"msg_{uuid.uuid4().hex[:24]}"
1218
+
1219
+ if request.stream:
1220
+ async def stream_generator():
1221
+ # message_start
1222
+ message_start = {
1223
+ "type": "message_start",
1224
+ "message": {
1225
+ "id": request_id,
1226
+ "type": "message",
1227
+ "role": "assistant",
1228
+ "model": request.model,
1229
+ "content": [],
1230
+ "stop_reason": None,
1231
+ "usage": {"input_tokens": 0, "output_tokens": 0}
1232
+ }
1233
+ }
1234
+ yield f"event: message_start\ndata: {json.dumps(message_start)}\n\n"
1235
+
1236
+ # content_block_start for text
1237
+ content_block_start = {
1238
+ "type": "content_block_start",
1239
+ "index": 0,
1240
+ "content_block": {"type": "text", "text": ""}
1241
+ }
1242
+ yield f"event: content_block_start\ndata: {json.dumps(content_block_start)}\n\n"
1243
+
1244
+ full_text = ""
1245
+ output_tokens = 0
1246
+
1247
+ async for token in generate_stream(
1248
+ prompt,
1249
+ max_tokens=request.max_tokens,
1250
+ temperature=request.temperature or 0.7,
1251
+ ):
1252
+ full_text += token
1253
+ output_tokens += 1
1254
+ delta = {
1255
+ "type": "content_block_delta",
1256
+ "index": 0,
1257
+ "delta": {"type": "text_delta", "text": token}
1258
+ }
1259
+ yield f"event: content_block_delta\ndata: {json.dumps(delta)}\n\n"
1260
+
1261
+ # content_block_stop for text
1262
+ yield f"event: content_block_stop\ndata: {json.dumps({'type': 'content_block_stop', 'index': 0})}\n\n"
1263
+
1264
+ # Check if we should emit a tool_use block
1265
+ tool_action = parse_computer_action_from_text(full_text, screen_width, screen_height)
1266
+ if tool_action:
1267
+ tool_block_start = {
1268
+ "type": "content_block_start",
1269
+ "index": 1,
1270
+ "content_block": {
1271
+ "type": "tool_use",
1272
+ "id": tool_action["id"],
1273
+ "name": tool_action["name"],
1274
+ "input": {}
1275
+ }
1276
+ }
1277
+ yield f"event: content_block_start\ndata: {json.dumps(tool_block_start)}\n\n"
1278
+
1279
+ # Send input as delta
1280
+ input_delta = {
1281
+ "type": "content_block_delta",
1282
+ "index": 1,
1283
+ "delta": {"type": "input_json_delta", "partial_json": json.dumps(tool_action["input"])}
1284
+ }
1285
+ yield f"event: content_block_delta\ndata: {json.dumps(input_delta)}\n\n"
1286
+
1287
+ yield f"event: content_block_stop\ndata: {json.dumps({'type': 'content_block_stop', 'index': 1})}\n\n"
1288
+
1289
+ # message_delta
1290
+ stop_reason = "tool_use" if tool_action else "end_turn"
1291
+ message_delta = {
1292
+ "type": "message_delta",
1293
+ "delta": {"stop_reason": stop_reason},
1294
+ "usage": {"output_tokens": output_tokens}
1295
+ }
1296
+ yield f"event: message_delta\ndata: {json.dumps(message_delta)}\n\n"
1297
+
1298
+ yield f"event: message_stop\ndata: {json.dumps({'type': 'message_stop'})}\n\n"
1299
+
1300
+ return StreamingResponse(
1301
+ stream_generator(),
1302
+ media_type="text/event-stream",
1303
+ headers={"Cache-Control": "no-cache", "Connection": "keep-alive"}
1304
+ )
1305
+
1306
+ # Non-streaming response
1307
+ response_text, thinking_text, input_tokens, output_tokens, stop_reason = generate_response(
1308
+ prompt,
1309
+ max_tokens=request.max_tokens,
1310
+ temperature=request.temperature or 0.7,
1311
+ )
1312
+
1313
+ full_response = prefill + response_text if prefill else response_text
1314
+
1315
+ # Build content blocks
1316
+ content_blocks = []
1317
+
1318
+ # Add text block
1319
+ content_blocks.append({"type": "text", "text": full_response})
1320
+
1321
+ # Parse and add tool use block if detected
1322
+ tool_action = parse_computer_action_from_text(full_response, screen_width, screen_height)
1323
+ if tool_action:
1324
+ content_blocks.append(tool_action)
1325
+ stop_reason = "tool_use"
1326
+ else:
1327
+ stop_reason = "end_turn"
1328
+
1329
+ return CUAResponse(
1330
+ id=request_id,
1331
+ model=request.model,
1332
+ content=content_blocks,
1333
+ stop_reason=stop_reason,
1334
+ usage={
1335
+ "input_tokens": input_tokens,
1336
+ "output_tokens": output_tokens
1337
+ }
1338
+ )
1339
+
1340
+
1341
+ # Alternative endpoint path for compatibility
1342
+ @app.post("/v1/computer-use")
1343
+ async def computer_use_alt(
1344
+ request: CUARequest,
1345
+ authorization: Optional[str] = Header(None),
1346
+ x_api_key: Optional[str] = Header(None, alias="x-api-key"),
1347
+ ):
1348
+ """Alternative endpoint path for computer use"""
1349
+ return await computer_use_agent(request, authorization, x_api_key)
1350
+
1351
+
1352
  # ============================================================================
1353
  # Health & Info Endpoints
1354
  # ============================================================================
 
1357
  async def root():
1358
  return {
1359
  "name": "Free Coding API",
1360
+ "version": "1.2.0",
1361
  "model": MODEL_ID,
1362
  "features": {
1363
  "prefill_response": "Supported - Include assistant message at end for output control",
1364
  "thinking": "Supported - Enable with thinking: {type: 'enabled'}",
1365
+ "streaming": "Supported - Both OpenAI and Anthropic formats",
1366
+ "computer_use": "Supported - CUA with sheikh-computer-use-preview model"
1367
  },
1368
  "compatibility": {
1369
  "openai": "v1 Chat Completions API",
1370
+ "anthropic": "Messages API (2023-06-01)",
1371
+ "computer_use": "Anthropic Computer Use API compatible"
1372
  },
1373
  "endpoints": {
1374
  "openai_chat": "/v1/chat/completions",
1375
  "anthropic_messages": "/v1/messages",
1376
+ "computer_use": "/v1/cua",
1377
+ "computer_use_alt": "/v1/computer-use",
1378
  "models": "/v1/models"
1379
  },
1380
+ "cua_model": "sheikh-computer-use-preview",
1381
  "docs": "/docs"
1382
  }
1383