1 line
75 KiB
JSON
1 line
75 KiB
JSON
{"repo": "bennyscash1/aicompautobe", "files": {"AiApiCompAuto/Infra/OpenAi/OpenAiService.cs": {"a9d7dccce4e85cac34be49e61537a7a1de37d145": {"t": 1785746193.4100628, "n": 18197, "h": "f6071e1d3cce9a6a", "c": "using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing OpenAI.Chat;\nusing System.Text;\nusing System.Text.Json;\n\nnamespace SafeCash.Test.ApiTest.Integration.OpenAi\n{\n public class OpenAiService\n {\n public static string openAiModel = \"gpt-4o-mini\";\n public static string grokAiMode = \"grok-3-mini-fast-latest\";\n #region SystemPrompt\n public enum SystemPromptTypeEnum\n {\n ApiRequest,\n MobileTextInpueRequest,\n MobileSystemPromptMissionTask,\n MobileXyCordinateRequest,\n ImagesCompare,\n AppPackageDetails,\n }\n string mobilePrePrompt = \"You are an automation expert.\\n\" +\n \"Given a mobile XML element dump, return only the XPath of the button or element best matching the target.\\n\\n\" +\n \"Rules:\\n\" +\n \"- Match by content-desc, text, or resource-id.\\n\" +\n \"- If provided, also consider color or visual clues (e.g., \\\"Blue icon\\\") when content-desc, text, or resource-id are insufficient.\\n\" +\n \"- One match → return XPath only. Multiple → return count. No match → return 0.\\n\" +\n \"- Allow partial matches (substring, typo, etc.).\\n\" +\n \"- Recognize semantic and visual terms (e.g., '3 dots', 'plus icon', 'person icon', color descriptions):\\n\" +\n \" • 'plus icon' → content-desc with 'Add'/'Create' or resource-id with 'fab'\\n\" +\n \" • 'search field' → EditText or 'Search'\\n\" +\n \" • 'person icon' → profile/avatar/user\\n\" +\n \" • 'menu button' or '3 dots' → 'More options' or 'Customize and control'\\n\" +\n \" • Color terms (e.g., \\\"Blue icon\\\") → prioritize clickable buttons (ImageButton, Button, ImageView) without text\\n\" +\n \" especially when stated clearly like FAB buttons or visually distinct elements\\n\" +\n \" • Also infer visual roles or icon types from user terms (e.g., 'record icon', 'dialpad icon', '10 dots') even if no exact match in content-desc/text\\n\" +\n \"- Layouts (FrameLayout, LinearLayout) are valid if clickable and labeled.\\n\\n\" +\n \"Examples:\\n\" +\n \"<android.widget.ImageButton content-desc=\\\"5\\\" />\\n\" +\n \"Target: 5\\n\" +\n \"Expected: //android.widget.ImageButton[@content-desc=\\\"5\\\"]\\n\\n\" +\n \"<android.widget.ImageView resource-id=\\\"com.app:id/profile_icon\\\" />\\n\" +\n \"Target: person icon\\n\" +\n \"Expected: //android.widget.ImageView[@resource-id=\\\"com.app:id/profile_icon\\\"]\\n\\n\" +\n \"<android.widget.Button content-desc=\\\"Add city\\\" resource-id=\\\"com.google.android.deskclock:id/fab\\\" />\\n\" +\n \"Target: plus icon\\n\" +\n \"Expected: //android.widget.Button[@content-desc=\\\"Add city\\\"]\\n\\n\" +\n \"<android.widget.ImageButton content-desc=\\\"key pad\\\" resource-id=\\\"com.google.android.dialer:id/dialpad_fab\\\" clickable=\\\"true\\\" />\\n\" +\n \"Target: Blue icon with 10 dots\\n\" +\n \"Expected: //android.widget.ImageButton[@resource-id=\\\"com.google.android.dialer:id/dialpad_fab\\\"]\\n\\n\" +\n \"Important: Return ONLY the raw XPath string (no markdown, no quotes, no formatting).\\n\" +\n \"Now analyze the following:\";\n\n string mobilePrePromptCordinateXy = \"You are an automation expert.\\n\" +\n \"Your task is:\\n\" +\n \"Given:\\n\" +\n \"- A full XML dump of a mobile screen hierarchy.\\n\" +\n \"- The screen width and height.\\n\" +\n \"- A pair of coordinates (X and Y).\\n\\n\" +\n \"Rules:\\n\" +\n \"1. Identify the single UI element in the XML whose bounds ([left,top][right,bottom]) fully contain the given X and Y coordinates.\\n\" +\n \"2. If multiple elements overlap, select the deepest (most nested) element.\\n\" +\n \"3. Return only the XPath expression to locate that specific element.\\n\" +\n \"4. XPath must be precise and use resource-id or content-desc if available.\\n\" +\n \" - Prefer resource-id when present.\\n\" +\n \" - If resource-id is missing, use content-desc.\\n\" +\n \" - If both are missing, use full tag and index path.\\n\" +\n \"5. If no matching element is found, return exactly the number 0 (zero) and nothing else.\\n\" +\n \"6. Never explain, apologize, or add any extra text.\\n\\n\" +\n \"Examples of valid responses:\\n\" +\n \"- //android.widget.ImageButton[@resource-id=\\\"com.example:id/button\\\"]\\n\" +\n \"- //android.widget.TextView[@content-desc=\\\"Submit\\\"]\\n\" +\n \"- 0\\n\\n\" +\n \"Important:\\n\" +\n \"- Screen coordinates are based on the screen size.\\n\" +\n \"- Bounds are given as [left,top][right,bottom] relative to the screen.\\n\" +\n \"- Coordinates on the exact border (equal to left or top but less than right or bottom) are considered inside the bounds.\\n\" +\n \"- Always return only one XPath or 0.\\n\" +\n \"7. Coordinates must be strictly inside the bounds:\\n \" +\n \"- X must be >= left and < right.\\n \" +\n \" - Y must be >= top and < bottom.\\n \" +\n \"- No partial matches are allowed.\\n \" +\n \"- Ignore elements where X and Y are not fully contained inside bounds.\\n\";\n\n string aiSystemPromptMissionTask =\n \"You are a smart navigation agent inside a mobile app.\\n\\n\" +\n \"Your task is to analyze the given Android XML UI hierarchy and guide the user step-by-step to complete their goal.\\n\\n\" +\n \"Input:\\n\" +\n \"1. xmlHierarchy – the current screen's XML.\\n\" +\n \"2. userGoal – a free-text description of what the user wants to do (e.g. 'Search for Hello World').\\n\\n\" +\n \"Process:\\n\" +\n \"- Parse the XML and check if the goal is already complete.\\n\" +\n \"- If the user goal is fully completed (all required inputs/actions are done), return:\\n\" +\n \" { \\\"type\\\": 3 }\\n\" +\n \"- If not, find the next visible UI element that moves the user closer to the goal.\\n\" +\n \"- Prefer buttons like 'Next', 'OK', or 'Continue' unless the goal says otherwise.\\n\" +\n \"- Only suggest actions that match real elements in the XML (no guesses).\\n\" +\n \"- Use exact XPaths based on class, text, content-desc, or resource-id.\\n\" +\n \"- Return only one step at a time – button click or input.\\n\" +\n \"- If stuck, return: { \\\"type\\\": 0 }\\n\\n\" +\n\n \"Output format:\\n\" +\n \"- Click: { \\\"type\\\": 1, \\\"xpath\\\": \\\"...\\\" }\\n\" +\n \"- Input: { \\\"type\\\": 2, \\\"xpath\\\": \\\"...\\\", \\\"value\\\": \\\"...\\\" }\\n\" +\n \"- Done: { \\\"type\\\": 3 }\\n\" +\n \"- No action: { \\\"type\\\": 0 }\\n\\n\" +\n\n \"Important:\\n\" +\n \"- If all required steps are marked as already clicked (via provided locators), return { \\\"type\\\": 3 } and do not continue.\\n\" +\n \"- Never return commentary or code blocks – only a valid JSON object.\\n\" +\n \"- Do not return suggestions for extra UI elements if not part of the userGoal.\\n\";\n\n\n string getAppPackageDetailsSystemPrompt =\n @\"You are an Android expert.\n Your task is to extract the appPackage and appActivity for a specific app name (e.g., 'camera' or 'contacts').\n\n You will receive:\n - A list of installed app packages\n - A target app name\n\n ---\n\n Your job is to:\n 1. Find the app package that best matches the target app name (case-insensitive, partial match is allowed).\n 2. Return a valid JSON in this exact format:\n {\n \"\"appPackage\"\": \"\"<appPackage>\"\",\n \"\"appActivity\"\": \"\"<appActivity>\"\"\n }\n\n ---\n\n Rules:\n - Respond with JSON only. No explanation, no extra text.\n - If multiple packages might match, return the best/most likely one.\n - If no match found, still return the JSON with empty values:\n {\n \"\"appPackage\"\": \"\"\"\",\n \"\"appActivity\"\": \"\"\"\"\n }\n \";\n\n public string GetSystemPrompt(SystemPromptTypeEnum aiRequest)\n {\n string prePrompt;\n switch (aiRequest)\n {\n case SystemPromptTypeEnum.MobileTextInpueRequest:\n prePrompt = mobilePrePrompt;\n break;\n case SystemPromptTypeEnum.MobileSystemPromptMissionTask:\n prePrompt = aiSystemPromptMissionTask;\n break;\n case SystemPromptTypeEnum.MobileXyCordinateRequest:\n prePrompt = mobilePrePromptCordinateXy;\n break;\n case SystemPromptTypeEnum.AppPackageDetails:\n prePrompt = getAppPackageDetailsSystemPrompt;\n break;\n\n default:\n prePrompt = \"You are an AI assistant.\"; // Default fallback\n break;\n }\n return prePrompt;\n }\n #endregion\n\n #region OpenAiServiceRequest\n\n public async Task<string> OpenAiServiceRequest(string userPrompts, SystemPromptTypeEnum systemPrompt)\n {\n string model = openAiModel;\n string prePrompt = GetSystemPrompt(systemPrompt);\n\n string apiKey = Environment.GetEnvironmentVariable(\"OPENAI_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n string apiResponce = \"An error occurred or no response was returned.\";\n //string combinedPrompt = $\"{prePrompt}\\n\\n{userPrompts}\";\n try\n {\n ChatClient client = new ChatClient(model, apiKey);\n\n var openAiRequest = new\n {\n model = model,\n messages = new[]\n {\n new { role = \"system\", content = prePrompt },\n new { role = \"user\", content = userPrompts }\n }\n };\n string jsonBody = JsonConvert.SerializeObject(openAiRequest, Formatting.Indented);\n\n UserChatMessage message = new UserChatMessage(jsonBody);\n ChatCompletion completion = await client.CompleteChatAsync(message);\n\n if (completion?.Content != null && completion.Content.Count > 0)\n {\n apiResponce = completion.Content[0].Text;\n\n }\n else\n {\n apiResponce = \"No valid content found in the response.\";\n }\n Console.WriteLine(\"after ai\");\n\n }\n catch (Exception ex)\n {\n Console.WriteLine(ex.Message);\n }\n\n return apiResponce;\n }\n #endregion\n\n #region Grok ai request\n public async Task<string> GrokRequestService(string userMessage, SystemPromptTypeEnum aiRequest)\n {\n string grokUrl = \"https://api.x.ai/v1/chat/completions\";\n string apiGrokKey = Environment.GetEnvironmentVariable(\"GROK_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n\n using (HttpClient client = new HttpClient())\n {\n string aiPrePromptType = GetSystemPrompt(aiRequest);\n\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiGrokKey}\");\n\n var requestBody = new\n {\n messages = new[]\n {\n new { role = \"system\", content = aiPrePromptType },\n new { role = \"user\", content = userMessage }\n },\n model = grokAiMode,\n stream = false,\n temperature = 0\n };\n\n var jsonContent = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, \"application/json\");\n HttpResponseMessage response = await client.PostAsync(grokUrl, jsonContent);\n\n if (response.IsSuccessStatusCode)\n {\n string jsonResponse = await response.Content.ReadAsStringAsync();\n\n // ✅ Extract only the \"content\" field from the response\n using JsonDocument doc = JsonDocument.Parse(jsonResponse);\n\n if (doc.RootElement.TryGetProperty(\"choices\", out JsonElement choicesArray) &&\n choicesArray.GetArrayLength() > 0 &&\n choicesArray[0].TryGetProperty(\"message\", out JsonElement message) &&\n message.TryGetProperty(\"content\", out JsonElement contentElement))\n {\n return contentElement.GetString() ?? \"No content available.\";\n }\n\n return \"Invalid response format.\";\n }\n else\n {\n return $\"Error: {response.StatusCode} - {response.ReasonPhrase}\";\n }\n }\n }\n #endregion\n\n #region Deep seek ai request\n private readonly HttpClient _httpClient = new HttpClient();\n\n public async Task<string> DeepSeekResponceAi(string userPrompts, SystemPromptTypeEnum aiRequest)\n {\n string apiUrl = \"https://api.deepseek.com/chat/completions\"; // Replace with actual API URL\n string bearerToken = \"sk-5706d7050b8c4bddb967ba236538d89d\"; // Replace with actual token\n string prePrompt = GetSystemPrompt(aiRequest);\n var requestBody = new\n {\n model = \"deepseek-chat\",\n messages = new[]\n {\n new { role = \"system\", content = prePrompt },\n new { role = \"user\", content = userPrompts }\n\n },\n stream = false\n };\n\n string jsonRequest = System.Text.Json.JsonSerializer.Serialize(requestBody);\n var content = new StringContent(jsonRequest, Encoding.UTF8, \"application/json\"); // ✅ \"Content-Type\" set correctly here\n\n var request = new HttpRequestMessage(HttpMethod.Post, apiUrl)\n {\n Content = content\n };\n\n request.Headers.Add(\"Authorization\", $\"Bearer {bearerToken}\"); // ✅ Correct place for Authorization\n // No need to add \"Content-Type\" again here\n\n HttpResponseMessage response = await _httpClient.SendAsync(request);\n string jsonResponse = await response.Content.ReadAsStringAsync();\n\n using JsonDocument doc = JsonDocument.Parse(jsonResponse);\n\n string? contentResponce = doc.RootElement\n .GetProperty(\"choices\")[0]\n .GetProperty(\"message\")\n .GetProperty(\"content\")\n .GetString();\n\n return contentResponce;\n }\n #endregion\n\n #region Claude ais request\n\n public async Task<string> GetClaudeResponse(string userPrompts, SystemPromptTypeEnum aiRequest,\n string apiKey = \"\")\n {\n string prePrompt = GetSystemPrompt(aiRequest);\n if (string.IsNullOrEmpty(apiKey))\n {\n //apiKey = Environment.GetEnvironmentVariable(\"CLAUDE_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n Console.WriteLine(\"Anthropic Api key is: \" +apiKey);\n throw new InvalidOperationException(\"API key is missing. Please provide a valid API key for Claude AI.\");\n }\n string url = \"https://api.anthropic.com/v1/messages\";\n string claudeModel = \"claude-3-5-sonnet-20240620\";\n var requestBody = new\n {\n model = \"claude-3-5-haiku-20241022\",\n max_tokens = 1024,\n system = prePrompt,\n messages = new[]\n {\n new { role = \"user\", content = userPrompts }\n }\n };\n\n var json = JsonConvert.SerializeObject(requestBody);\n\n // Use the retry method with fresh request creation\n var response = await SendWithRetries(json, url, apiKey);\n\n string responseContent = await response.Content.ReadAsStringAsync();\n\n if (!response.IsSuccessStatusCode)\n {\n throw new InvalidOperationException($\"API key '{apiKey}' is incorrect (Anthropic return http status {response.StatusCode}, {responseContent}). \" +\n $\"Please provide a valid API key for Claude AI.\");\n //return $\"Error: {response.StatusCode}\\n{responseContent}\";\n }\n\n JObject parsed = JObject.Parse(responseContent);\n var text = parsed[\"content\"]?[0]?[\"text\"]?.ToString();\n\n return text ?? \"No text content found.\";\n }\n\n private async Task<HttpResponseMessage> SendWithRetries(string json, string url, string apiKey, int maxRetries = 3)\n {\n for (int attempt = 0; attempt < maxRetries; attempt++)\n {\n var request = new HttpRequestMessage(HttpMethod.Post, url);\n request.Headers.Add(\"x-api-key\", apiKey);\n request.Headers.Add(\"anthropic-version\", \"2023-06-01\");\n request.Content = new StringContent(json, Encoding.UTF8, \"application/json\");\n\n var response = await _httpClient.SendAsync(request);\n\n if ((int)response.StatusCode != 529)\n return response;\n\n int waitTime = (int)Math.Pow(2, attempt + 1) * 1000; // 2s, 4s, 8s\n await Task.Delay(waitTime);\n }\n\n throw new Exception(\"Claude API is overloaded (529) after multiple retries.\");\n }\n\n #endregion\n }\n public enum AiServicesList\n {\n OpenAi,\n Grok,\n Claude,\n DeepSeek\n }\n}\n"}, "c74e9e020b8e59e37020665bd4774493551757e7": {"t": 1785760510.576776, "n": 18197, "h": "f6071e1d3cce9a6a", "c": "using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing OpenAI.Chat;\nusing System.Text;\nusing System.Text.Json;\n\nnamespace SafeCash.Test.ApiTest.Integration.OpenAi\n{\n public class OpenAiService\n {\n public static string openAiModel = \"gpt-4o-mini\";\n public static string grokAiMode = \"grok-3-mini-fast-latest\";\n #region SystemPrompt\n public enum SystemPromptTypeEnum\n {\n ApiRequest,\n MobileTextInpueRequest,\n MobileSystemPromptMissionTask,\n MobileXyCordinateRequest,\n ImagesCompare,\n AppPackageDetails,\n }\n string mobilePrePrompt = \"You are an automation expert.\\n\" +\n \"Given a mobile XML element dump, return only the XPath of the button or element best matching the target.\\n\\n\" +\n \"Rules:\\n\" +\n \"- Match by content-desc, text, or resource-id.\\n\" +\n \"- If provided, also consider color or visual clues (e.g., \\\"Blue icon\\\") when content-desc, text, or resource-id are insufficient.\\n\" +\n \"- One match → return XPath only. Multiple → return count. No match → return 0.\\n\" +\n \"- Allow partial matches (substring, typo, etc.).\\n\" +\n \"- Recognize semantic and visual terms (e.g., '3 dots', 'plus icon', 'person icon', color descriptions):\\n\" +\n \" • 'plus icon' → content-desc with 'Add'/'Create' or resource-id with 'fab'\\n\" +\n \" • 'search field' → EditText or 'Search'\\n\" +\n \" • 'person icon' → profile/avatar/user\\n\" +\n \" • 'menu button' or '3 dots' → 'More options' or 'Customize and control'\\n\" +\n \" • Color terms (e.g., \\\"Blue icon\\\") → prioritize clickable buttons (ImageButton, Button, ImageView) without text\\n\" +\n \" especially when stated clearly like FAB buttons or visually distinct elements\\n\" +\n \" • Also infer visual roles or icon types from user terms (e.g., 'record icon', 'dialpad icon', '10 dots') even if no exact match in content-desc/text\\n\" +\n \"- Layouts (FrameLayout, LinearLayout) are valid if clickable and labeled.\\n\\n\" +\n \"Examples:\\n\" +\n \"<android.widget.ImageButton content-desc=\\\"5\\\" />\\n\" +\n \"Target: 5\\n\" +\n \"Expected: //android.widget.ImageButton[@content-desc=\\\"5\\\"]\\n\\n\" +\n \"<android.widget.ImageView resource-id=\\\"com.app:id/profile_icon\\\" />\\n\" +\n \"Target: person icon\\n\" +\n \"Expected: //android.widget.ImageView[@resource-id=\\\"com.app:id/profile_icon\\\"]\\n\\n\" +\n \"<android.widget.Button content-desc=\\\"Add city\\\" resource-id=\\\"com.google.android.deskclock:id/fab\\\" />\\n\" +\n \"Target: plus icon\\n\" +\n \"Expected: //android.widget.Button[@content-desc=\\\"Add city\\\"]\\n\\n\" +\n \"<android.widget.ImageButton content-desc=\\\"key pad\\\" resource-id=\\\"com.google.android.dialer:id/dialpad_fab\\\" clickable=\\\"true\\\" />\\n\" +\n \"Target: Blue icon with 10 dots\\n\" +\n \"Expected: //android.widget.ImageButton[@resource-id=\\\"com.google.android.dialer:id/dialpad_fab\\\"]\\n\\n\" +\n \"Important: Return ONLY the raw XPath string (no markdown, no quotes, no formatting).\\n\" +\n \"Now analyze the following:\";\n\n string mobilePrePromptCordinateXy = \"You are an automation expert.\\n\" +\n \"Your task is:\\n\" +\n \"Given:\\n\" +\n \"- A full XML dump of a mobile screen hierarchy.\\n\" +\n \"- The screen width and height.\\n\" +\n \"- A pair of coordinates (X and Y).\\n\\n\" +\n \"Rules:\\n\" +\n \"1. Identify the single UI element in the XML whose bounds ([left,top][right,bottom]) fully contain the given X and Y coordinates.\\n\" +\n \"2. If multiple elements overlap, select the deepest (most nested) element.\\n\" +\n \"3. Return only the XPath expression to locate that specific element.\\n\" +\n \"4. XPath must be precise and use resource-id or content-desc if available.\\n\" +\n \" - Prefer resource-id when present.\\n\" +\n \" - If resource-id is missing, use content-desc.\\n\" +\n \" - If both are missing, use full tag and index path.\\n\" +\n \"5. If no matching element is found, return exactly the number 0 (zero) and nothing else.\\n\" +\n \"6. Never explain, apologize, or add any extra text.\\n\\n\" +\n \"Examples of valid responses:\\n\" +\n \"- //android.widget.ImageButton[@resource-id=\\\"com.example:id/button\\\"]\\n\" +\n \"- //android.widget.TextView[@content-desc=\\\"Submit\\\"]\\n\" +\n \"- 0\\n\\n\" +\n \"Important:\\n\" +\n \"- Screen coordinates are based on the screen size.\\n\" +\n \"- Bounds are given as [left,top][right,bottom] relative to the screen.\\n\" +\n \"- Coordinates on the exact border (equal to left or top but less than right or bottom) are considered inside the bounds.\\n\" +\n \"- Always return only one XPath or 0.\\n\" +\n \"7. Coordinates must be strictly inside the bounds:\\n \" +\n \"- X must be >= left and < right.\\n \" +\n \" - Y must be >= top and < bottom.\\n \" +\n \"- No partial matches are allowed.\\n \" +\n \"- Ignore elements where X and Y are not fully contained inside bounds.\\n\";\n\n string aiSystemPromptMissionTask =\n \"You are a smart navigation agent inside a mobile app.\\n\\n\" +\n \"Your task is to analyze the given Android XML UI hierarchy and guide the user step-by-step to complete their goal.\\n\\n\" +\n \"Input:\\n\" +\n \"1. xmlHierarchy – the current screen's XML.\\n\" +\n \"2. userGoal – a free-text description of what the user wants to do (e.g. 'Search for Hello World').\\n\\n\" +\n \"Process:\\n\" +\n \"- Parse the XML and check if the goal is already complete.\\n\" +\n \"- If the user goal is fully completed (all required inputs/actions are done), return:\\n\" +\n \" { \\\"type\\\": 3 }\\n\" +\n \"- If not, find the next visible UI element that moves the user closer to the goal.\\n\" +\n \"- Prefer buttons like 'Next', 'OK', or 'Continue' unless the goal says otherwise.\\n\" +\n \"- Only suggest actions that match real elements in the XML (no guesses).\\n\" +\n \"- Use exact XPaths based on class, text, content-desc, or resource-id.\\n\" +\n \"- Return only one step at a time – button click or input.\\n\" +\n \"- If stuck, return: { \\\"type\\\": 0 }\\n\\n\" +\n\n \"Output format:\\n\" +\n \"- Click: { \\\"type\\\": 1, \\\"xpath\\\": \\\"...\\\" }\\n\" +\n \"- Input: { \\\"type\\\": 2, \\\"xpath\\\": \\\"...\\\", \\\"value\\\": \\\"...\\\" }\\n\" +\n \"- Done: { \\\"type\\\": 3 }\\n\" +\n \"- No action: { \\\"type\\\": 0 }\\n\\n\" +\n\n \"Important:\\n\" +\n \"- If all required steps are marked as already clicked (via provided locators), return { \\\"type\\\": 3 } and do not continue.\\n\" +\n \"- Never return commentary or code blocks – only a valid JSON object.\\n\" +\n \"- Do not return suggestions for extra UI elements if not part of the userGoal.\\n\";\n\n\n string getAppPackageDetailsSystemPrompt =\n @\"You are an Android expert.\n Your task is to extract the appPackage and appActivity for a specific app name (e.g., 'camera' or 'contacts').\n\n You will receive:\n - A list of installed app packages\n - A target app name\n\n ---\n\n Your job is to:\n 1. Find the app package that best matches the target app name (case-insensitive, partial match is allowed).\n 2. Return a valid JSON in this exact format:\n {\n \"\"appPackage\"\": \"\"<appPackage>\"\",\n \"\"appActivity\"\": \"\"<appActivity>\"\"\n }\n\n ---\n\n Rules:\n - Respond with JSON only. No explanation, no extra text.\n - If multiple packages might match, return the best/most likely one.\n - If no match found, still return the JSON with empty values:\n {\n \"\"appPackage\"\": \"\"\"\",\n \"\"appActivity\"\": \"\"\"\"\n }\n \";\n\n public string GetSystemPrompt(SystemPromptTypeEnum aiRequest)\n {\n string prePrompt;\n switch (aiRequest)\n {\n case SystemPromptTypeEnum.MobileTextInpueRequest:\n prePrompt = mobilePrePrompt;\n break;\n case SystemPromptTypeEnum.MobileSystemPromptMissionTask:\n prePrompt = aiSystemPromptMissionTask;\n break;\n case SystemPromptTypeEnum.MobileXyCordinateRequest:\n prePrompt = mobilePrePromptCordinateXy;\n break;\n case SystemPromptTypeEnum.AppPackageDetails:\n prePrompt = getAppPackageDetailsSystemPrompt;\n break;\n\n default:\n prePrompt = \"You are an AI assistant.\"; // Default fallback\n break;\n }\n return prePrompt;\n }\n #endregion\n\n #region OpenAiServiceRequest\n\n public async Task<string> OpenAiServiceRequest(string userPrompts, SystemPromptTypeEnum systemPrompt)\n {\n string model = openAiModel;\n string prePrompt = GetSystemPrompt(systemPrompt);\n\n string apiKey = Environment.GetEnvironmentVariable(\"OPENAI_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n string apiResponce = \"An error occurred or no response was returned.\";\n //string combinedPrompt = $\"{prePrompt}\\n\\n{userPrompts}\";\n try\n {\n ChatClient client = new ChatClient(model, apiKey);\n\n var openAiRequest = new\n {\n model = model,\n messages = new[]\n {\n new { role = \"system\", content = prePrompt },\n new { role = \"user\", content = userPrompts }\n }\n };\n string jsonBody = JsonConvert.SerializeObject(openAiRequest, Formatting.Indented);\n\n UserChatMessage message = new UserChatMessage(jsonBody);\n ChatCompletion completion = await client.CompleteChatAsync(message);\n\n if (completion?.Content != null && completion.Content.Count > 0)\n {\n apiResponce = completion.Content[0].Text;\n\n }\n else\n {\n apiResponce = \"No valid content found in the response.\";\n }\n Console.WriteLine(\"after ai\");\n\n }\n catch (Exception ex)\n {\n Console.WriteLine(ex.Message);\n }\n\n return apiResponce;\n }\n #endregion\n\n #region Grok ai request\n public async Task<string> GrokRequestService(string userMessage, SystemPromptTypeEnum aiRequest)\n {\n string grokUrl = \"https://api.x.ai/v1/chat/completions\";\n string apiGrokKey = Environment.GetEnvironmentVariable(\"GROK_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n\n using (HttpClient client = new HttpClient())\n {\n string aiPrePromptType = GetSystemPrompt(aiRequest);\n\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiGrokKey}\");\n\n var requestBody = new\n {\n messages = new[]\n {\n new { role = \"system\", content = aiPrePromptType },\n new { role = \"user\", content = userMessage }\n },\n model = grokAiMode,\n stream = false,\n temperature = 0\n };\n\n var jsonContent = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, \"application/json\");\n HttpResponseMessage response = await client.PostAsync(grokUrl, jsonContent);\n\n if (response.IsSuccessStatusCode)\n {\n string jsonResponse = await response.Content.ReadAsStringAsync();\n\n // ✅ Extract only the \"content\" field from the response\n using JsonDocument doc = JsonDocument.Parse(jsonResponse);\n\n if (doc.RootElement.TryGetProperty(\"choices\", out JsonElement choicesArray) &&\n choicesArray.GetArrayLength() > 0 &&\n choicesArray[0].TryGetProperty(\"message\", out JsonElement message) &&\n message.TryGetProperty(\"content\", out JsonElement contentElement))\n {\n return contentElement.GetString() ?? \"No content available.\";\n }\n\n return \"Invalid response format.\";\n }\n else\n {\n return $\"Error: {response.StatusCode} - {response.ReasonPhrase}\";\n }\n }\n }\n #endregion\n\n #region Deep seek ai request\n private readonly HttpClient _httpClient = new HttpClient();\n\n public async Task<string> DeepSeekResponceAi(string userPrompts, SystemPromptTypeEnum aiRequest)\n {\n string apiUrl = \"https://api.deepseek.com/chat/completions\"; // Replace with actual API URL\n string bearerToken = \"sk-5706d7050b8c4bddb967ba236538d89d\"; // Replace with actual token\n string prePrompt = GetSystemPrompt(aiRequest);\n var requestBody = new\n {\n model = \"deepseek-chat\",\n messages = new[]\n {\n new { role = \"system\", content = prePrompt },\n new { role = \"user\", content = userPrompts }\n\n },\n stream = false\n };\n\n string jsonRequest = System.Text.Json.JsonSerializer.Serialize(requestBody);\n var content = new StringContent(jsonRequest, Encoding.UTF8, \"application/json\"); // ✅ \"Content-Type\" set correctly here\n\n var request = new HttpRequestMessage(HttpMethod.Post, apiUrl)\n {\n Content = content\n };\n\n request.Headers.Add(\"Authorization\", $\"Bearer {bearerToken}\"); // ✅ Correct place for Authorization\n // No need to add \"Content-Type\" again here\n\n HttpResponseMessage response = await _httpClient.SendAsync(request);\n string jsonResponse = await response.Content.ReadAsStringAsync();\n\n using JsonDocument doc = JsonDocument.Parse(jsonResponse);\n\n string? contentResponce = doc.RootElement\n .GetProperty(\"choices\")[0]\n .GetProperty(\"message\")\n .GetProperty(\"content\")\n .GetString();\n\n return contentResponce;\n }\n #endregion\n\n #region Claude ais request\n\n public async Task<string> GetClaudeResponse(string userPrompts, SystemPromptTypeEnum aiRequest,\n string apiKey = \"\")\n {\n string prePrompt = GetSystemPrompt(aiRequest);\n if (string.IsNullOrEmpty(apiKey))\n {\n //apiKey = Environment.GetEnvironmentVariable(\"CLAUDE_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n Console.WriteLine(\"Anthropic Api key is: \" +apiKey);\n throw new InvalidOperationException(\"API key is missing. Please provide a valid API key for Claude AI.\");\n }\n string url = \"https://api.anthropic.com/v1/messages\";\n string claudeModel = \"claude-3-5-sonnet-20240620\";\n var requestBody = new\n {\n model = \"claude-3-5-haiku-20241022\",\n max_tokens = 1024,\n system = prePrompt,\n messages = new[]\n {\n new { role = \"user\", content = userPrompts }\n }\n };\n\n var json = JsonConvert.SerializeObject(requestBody);\n\n // Use the retry method with fresh request creation\n var response = await SendWithRetries(json, url, apiKey);\n\n string responseContent = await response.Content.ReadAsStringAsync();\n\n if (!response.IsSuccessStatusCode)\n {\n throw new InvalidOperationException($\"API key '{apiKey}' is incorrect (Anthropic return http status {response.StatusCode}, {responseContent}). \" +\n $\"Please provide a valid API key for Claude AI.\");\n //return $\"Error: {response.StatusCode}\\n{responseContent}\";\n }\n\n JObject parsed = JObject.Parse(responseContent);\n var text = parsed[\"content\"]?[0]?[\"text\"]?.ToString();\n\n return text ?? \"No text content found.\";\n }\n\n private async Task<HttpResponseMessage> SendWithRetries(string json, string url, string apiKey, int maxRetries = 3)\n {\n for (int attempt = 0; attempt < maxRetries; attempt++)\n {\n var request = new HttpRequestMessage(HttpMethod.Post, url);\n request.Headers.Add(\"x-api-key\", apiKey);\n request.Headers.Add(\"anthropic-version\", \"2023-06-01\");\n request.Content = new StringContent(json, Encoding.UTF8, \"application/json\");\n\n var response = await _httpClient.SendAsync(request);\n\n if ((int)response.StatusCode != 529)\n return response;\n\n int waitTime = (int)Math.Pow(2, attempt + 1) * 1000; // 2s, 4s, 8s\n await Task.Delay(waitTime);\n }\n\n throw new Exception(\"Claude API is overloaded (529) after multiple retries.\");\n }\n\n #endregion\n }\n public enum AiServicesList\n {\n OpenAi,\n Grok,\n Claude,\n DeepSeek\n }\n}\n"}, "a78b976326cb913f156714e6a75568ade0ba8d86": {"t": 1785760511.9459245, "n": 18186, "h": "65f389cbdb729a52", "c": "using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing OpenAI.Chat;\nusing System.Text;\nusing System.Text.Json;\n\nnamespace SafeCash.Test.ApiTest.Integration.OpenAi\n{\n public class OpenAiService\n {\n public static string openAiModel = \"gpt-4o-mini\";\n public static string grokAiMode = \"grok-3-mini-fast-latest\";\n #region SystemPrompt\n public enum SystemPromptTypeEnum\n {\n ApiRequest,\n MobileTextInpueRequest,\n MobileSystemPromptMissionTask,\n MobileXyCordinateRequest,\n ImagesCompare,\n AppPackageDetails,\n }\n string mobilePrePrompt = \"You are an automation expert.\\n\" +\n \"Given a mobile XML element dump, return only the XPath of the button or element best matching the target.\\n\\n\" +\n \"Rules:\\n\" +\n \"- Match by content-desc, text, or resource-id.\\n\" +\n \"- If provided, also consider color or visual clues (e.g., \\\"Blue icon\\\") when content-desc, text, or resource-id are insufficient.\\n\" +\n \"- One match → return XPath only. Multiple → return count. No match → return 0.\\n\" +\n \"- Allow partial matches (substring, typo, etc.).\\n\" +\n \"- Recognize semantic and visual terms (e.g., '3 dots', 'plus icon', 'person icon', color descriptions):\\n\" +\n \" • 'plus icon' → content-desc with 'Add'/'Create' or resource-id with 'fab'\\n\" +\n \" • 'search field' → EditText or 'Search'\\n\" +\n \" • 'person icon' → profile/avatar/user\\n\" +\n \" • 'menu button' or '3 dots' → 'More options' or 'Customize and control'\\n\" +\n \" • Color terms (e.g., \\\"Blue icon\\\") → prioritize clickable buttons (ImageButton, Button, ImageView) without text\\n\" +\n \" especially when stated clearly like FAB buttons or visually distinct elements\\n\" +\n \" • Also infer visual roles or icon types from user terms (e.g., 'record icon', 'dialpad icon', '10 dots') even if no exact match in content-desc/text\\n\" +\n \"- Layouts (FrameLayout, LinearLayout) are valid if clickable and labeled.\\n\\n\" +\n \"Examples:\\n\" +\n \"<android.widget.ImageButton content-desc=\\\"5\\\" />\\n\" +\n \"Target: 5\\n\" +\n \"Expected: //android.widget.ImageButton[@content-desc=\\\"5\\\"]\\n\\n\" +\n \"<android.widget.ImageView resource-id=\\\"com.app:id/profile_icon\\\" />\\n\" +\n \"Target: person icon\\n\" +\n \"Expected: //android.widget.ImageView[@resource-id=\\\"com.app:id/profile_icon\\\"]\\n\\n\" +\n \"<android.widget.Button content-desc=\\\"Add city\\\" resource-id=\\\"com.google.android.deskclock:id/fab\\\" />\\n\" +\n \"Target: plus icon\\n\" +\n \"Expected: //android.widget.Button[@content-desc=\\\"Add city\\\"]\\n\\n\" +\n \"<android.widget.ImageButton content-desc=\\\"key pad\\\" resource-id=\\\"com.google.android.dialer:id/dialpad_fab\\\" clickable=\\\"true\\\" />\\n\" +\n \"Target: Blue icon with 10 dots\\n\" +\n \"Expected: //android.widget.ImageButton[@resource-id=\\\"com.google.android.dialer:id/dialpad_fab\\\"]\\n\\n\" +\n \"Important: Return ONLY the raw XPath string (no markdown, no quotes, no formatting).\\n\" +\n \"Now analyze the following:\";\n\n string mobilePrePromptCordinateXy = \"You are an automation expert.\\n\" +\n \"Your task is:\\n\" +\n \"Given:\\n\" +\n \"- A full XML dump of a mobile screen hierarchy.\\n\" +\n \"- The screen width and height.\\n\" +\n \"- A pair of coordinates (X and Y).\\n\\n\" +\n \"Rules:\\n\" +\n \"1. Identify the single UI element in the XML whose bounds ([left,top][right,bottom]) fully contain the given X and Y coordinates.\\n\" +\n \"2. If multiple elements overlap, select the deepest (most nested) element.\\n\" +\n \"3. Return only the XPath expression to locate that specific element.\\n\" +\n \"4. XPath must be precise and use resource-id or content-desc if available.\\n\" +\n \" - Prefer resource-id when present.\\n\" +\n \" - If resource-id is missing, use content-desc.\\n\" +\n \" - If both are missing, use full tag and index path.\\n\" +\n \"5. If no matching element is found, return exactly the number 0 (zero) and nothing else.\\n\" +\n \"6. Never explain, apologize, or add any extra text.\\n\\n\" +\n \"Examples of valid responses:\\n\" +\n \"- //android.widget.ImageButton[@resource-id=\\\"com.example:id/button\\\"]\\n\" +\n \"- //android.widget.TextView[@content-desc=\\\"Submit\\\"]\\n\" +\n \"- 0\\n\\n\" +\n \"Important:\\n\" +\n \"- Screen coordinates are based on the screen size.\\n\" +\n \"- Bounds are given as [left,top][right,bottom] relative to the screen.\\n\" +\n \"- Coordinates on the exact border (equal to left or top but less than right or bottom) are considered inside the bounds.\\n\" +\n \"- Always return only one XPath or 0.\\n\" +\n \"7. Coordinates must be strictly inside the bounds:\\n \" +\n \"- X must be >= left and < right.\\n \" +\n \" - Y must be >= top and < bottom.\\n \" +\n \"- No partial matches are allowed.\\n \" +\n \"- Ignore elements where X and Y are not fully contained inside bounds.\\n\";\n\n string aiSystemPromptMissionTask =\n \"You are a smart navigation agent inside a mobile app.\\n\\n\" +\n \"Your task is to analyze the given Android XML UI hierarchy and guide the user step-by-step to complete their goal.\\n\\n\" +\n \"Input:\\n\" +\n \"1. xmlHierarchy – the current screen's XML.\\n\" +\n \"2. userGoal – a free-text description of what the user wants to do (e.g. 'Search for Hello World').\\n\\n\" +\n \"Process:\\n\" +\n \"- Parse the XML and check if the goal is already complete.\\n\" +\n \"- If the user goal is fully completed (all required inputs/actions are done), return:\\n\" +\n \" { \\\"type\\\": 3 }\\n\" +\n \"- If not, find the next visible UI element that moves the user closer to the goal.\\n\" +\n \"- Prefer buttons like 'Next', 'OK', or 'Continue' unless the goal says otherwise.\\n\" +\n \"- Only suggest actions that match real elements in the XML (no guesses).\\n\" +\n \"- Use exact XPaths based on class, text, content-desc, or resource-id.\\n\" +\n \"- Return only one step at a time – button click or input.\\n\" +\n \"- If stuck, return: { \\\"type\\\": 0 }\\n\\n\" +\n\n \"Output format:\\n\" +\n \"- Click: { \\\"type\\\": 1, \\\"xpath\\\": \\\"...\\\" }\\n\" +\n \"- Input: { \\\"type\\\": 2, \\\"xpath\\\": \\\"...\\\", \\\"value\\\": \\\"...\\\" }\\n\" +\n \"- Done: { \\\"type\\\": 3 }\\n\" +\n \"- No action: { \\\"type\\\": 0 }\\n\\n\" +\n\n \"Important:\\n\" +\n \"- If all required steps are marked as already clicked (via provided locators), return { \\\"type\\\": 3 } and do not continue.\\n\" +\n \"- Never return commentary or code blocks – only a valid JSON object.\\n\" +\n \"- Do not return suggestions for extra UI elements if not part of the userGoal.\\n\";\n\n\n string getAppPackageDetailsSystemPrompt =\n @\"You are an Android expert.\n Your task is to extract the appPackage and appActivity for a specific app name (e.g., 'camera' or 'contacts').\n\n You will receive:\n - A list of installed app packages\n - A target app name\n\n ---\n\n Your job is to:\n 1. Find the app package that best matches the target app name (case-insensitive, partial match is allowed).\n 2. Return a valid JSON in this exact format:\n {\n \"\"appPackage\"\": \"\"<appPackage>\"\",\n \"\"appActivity\"\": \"\"<appActivity>\"\"\n }\n\n ---\n\n Rules:\n - Respond with JSON only. No explanation, no extra text.\n - If multiple packages might match, return the best/most likely one.\n - If no match found, still return the JSON with empty values:\n {\n \"\"appPackage\"\": \"\"\"\",\n \"\"appActivity\"\": \"\"\"\"\n }\n \";\n\n public string GetSystemPrompt(SystemPromptTypeEnum aiRequest)\n {\n string prePrompt;\n switch (aiRequest)\n {\n case SystemPromptTypeEnum.MobileTextInpueRequest:\n prePrompt = mobilePrePrompt;\n break;\n case SystemPromptTypeEnum.MobileSystemPromptMissionTask:\n prePrompt = aiSystemPromptMissionTask;\n break;\n case SystemPromptTypeEnum.MobileXyCordinateRequest:\n prePrompt = mobilePrePromptCordinateXy;\n break;\n case SystemPromptTypeEnum.AppPackageDetails:\n prePrompt = getAppPackageDetailsSystemPrompt;\n break;\n\n default:\n prePrompt = \"You are an AI assistant.\"; // Default fallback\n break;\n }\n return prePrompt;\n }\n #endregion\n\n #region OpenAiServiceRequest\n\n public async Task<string> OpenAiServiceRequest(string userPrompts, SystemPromptTypeEnum systemPrompt)\n {\n string model = openAiModel;\n string prePrompt = GetSystemPrompt(systemPrompt);\n\n string apiKey = Environment.GetEnvironmentVariable(\"OPENAI_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n string apiResponce = \"An error occurred or no response was returned.\";\n //string combinedPrompt = $\"{prePrompt}\\n\\n{userPrompts}\";\n try\n {\n ChatClient client = new ChatClient(model, apiKey);\n\n var openAiRequest = new\n {\n model = model,\n messages = new[]\n {\n new { role = \"system\", content = prePrompt },\n new { role = \"user\", content = userPrompts }\n }\n };\n string jsonBody = JsonConvert.SerializeObject(openAiRequest, Formatting.Indented);\n\n UserChatMessage message = new UserChatMessage(jsonBody);\n ChatCompletion completion = await client.CompleteChatAsync(message);\n\n if (completion?.Content != null && completion.Content.Count > 0)\n {\n apiResponce = completion.Content[0].Text;\n\n }\n else\n {\n apiResponce = \"No valid content found in the response.\";\n }\n Console.WriteLine(\"after ai\");\n\n }\n catch (Exception ex)\n {\n Console.WriteLine(ex.Message);\n }\n\n return apiResponce;\n }\n #endregion\n\n #region Grok ai request\n public async Task<string> GrokRequestService(string userMessage, SystemPromptTypeEnum aiRequest)\n {\n string grokUrl = \"https://api.x.ai/v1/chat/completions\";\n string apiGrokKey = Environment.GetEnvironmentVariable(\"GROK_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n\n using (HttpClient client = new HttpClient())\n {\n string aiPrePromptType = GetSystemPrompt(aiRequest);\n\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiGrokKey}\");\n\n var requestBody = new\n {\n messages = new[]\n {\n new { role = \"system\", content = aiPrePromptType },\n new { role = \"user\", content = userMessage }\n },\n model = grokAiMode,\n stream = false,\n temperature = 0\n };\n\n var jsonContent = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, \"application/json\");\n HttpResponseMessage response = await client.PostAsync(grokUrl, jsonContent);\n\n if (response.IsSuccessStatusCode)\n {\n string jsonResponse = await response.Content.ReadAsStringAsync();\n\n // ✅ Extract only the \"content\" field from the response\n using JsonDocument doc = JsonDocument.Parse(jsonResponse);\n\n if (doc.RootElement.TryGetProperty(\"choices\", out JsonElement choicesArray) &&\n choicesArray.GetArrayLength() > 0 &&\n choicesArray[0].TryGetProperty(\"message\", out JsonElement message) &&\n message.TryGetProperty(\"content\", out JsonElement contentElement))\n {\n return contentElement.GetString() ?? \"No content available.\";\n }\n\n return \"Invalid response format.\";\n }\n else\n {\n return $\"Error: {response.StatusCode} - {response.ReasonPhrase}\";\n }\n }\n }\n #endregion\n\n #region Deep seek ai request\n private readonly HttpClient _httpClient = new HttpClient();\n\n public async Task<string> DeepSeekResponceAi(string userPrompts, SystemPromptTypeEnum aiRequest)\n {\n string apiUrl = \"https://api.deepseek.com/chat/completions\"; // Replace with actual API URL\n string bearerToken = \"sk-5706d7050b8c4bddb967ba236538d89d\"; // Replace with actual token\n string prePrompt = GetSystemPrompt(aiRequest);\n var requestBody = new\n {\n model = \"deepseek-chat\",\n messages = new[]\n {\n new { role = \"system\", content = prePrompt },\n new { role = \"user\", content = userPrompts }\n\n },\n stream = false\n };\n\n string jsonRequest = System.Text.Json.JsonSerializer.Serialize(requestBody);\n var content = new StringContent(jsonRequest, Encoding.UTF8, \"application/json\"); // ✅ \"Content-Type\" set correctly here\n\n var request = new HttpRequestMessage(HttpMethod.Post, apiUrl)\n {\n Content = content\n };\n\n request.Headers.Add(\"Authorization\", $\"Bearer {bearerToken}\"); // ✅ Correct place for Authorization\n // No need to add \"Content-Type\" again here\n\n HttpResponseMessage response = await _httpClient.SendAsync(request);\n string jsonResponse = await response.Content.ReadAsStringAsync();\n\n using JsonDocument doc = JsonDocument.Parse(jsonResponse);\n\n string? contentResponce = doc.RootElement\n .GetProperty(\"choices\")[0]\n .GetProperty(\"message\")\n .GetProperty(\"content\")\n .GetString();\n\n return contentResponce;\n }\n #endregion\n\n #region Claude ais request\n\n public async Task<string> GetClaudeResponse(string userPrompts, SystemPromptTypeEnum aiRequest,\n string apiKey = \"\")\n {\n string prePrompt = GetSystemPrompt(aiRequest);\n if (string.IsNullOrEmpty(apiKey))\n {\n //apiKey = Environment.GetEnvironmentVariable(\"CLAUDE_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n Console.WriteLine(\"Anthropic Api key is: \" +apiKey);\n throw new InvalidOperationException(\"API key is missing. Please provide a valid API key for Claude AI.\");\n }\n string url = \"https://api.anthropic.com/v1/messages\";\n string claudeModel = \"claude-3-5-sonnet-20240620\";\n var requestBody = new\n {\n model = \"claude-3-5-haiku-20241022\",\n max_tokens = 1024,\n system = prePrompt,\n messages = new[]\n {\n new { role = \"user\", content = userPrompts }\n }\n };\n\n var json = JsonConvert.SerializeObject(requestBody);\n\n // Use the retry method with fresh request creation\n var response = await SendWithRetries(json, url, apiKey);\n\n string responseContent = await response.Content.ReadAsStringAsync();\n\n if (!response.IsSuccessStatusCode)\n {\n throw new InvalidOperationException($\"API key is incorrect (Anthropic return http status {response.StatusCode}, {responseContent}). \" +\n $\"Please provide a valid API key for Claude AI.\");\n //return $\"Error: {response.StatusCode}\\n{responseContent}\";\n }\n\n JObject parsed = JObject.Parse(responseContent);\n var text = parsed[\"content\"]?[0]?[\"text\"]?.ToString();\n\n return text ?? \"No text content found.\";\n }\n\n private async Task<HttpResponseMessage> SendWithRetries(string json, string url, string apiKey, int maxRetries = 3)\n {\n for (int attempt = 0; attempt < maxRetries; attempt++)\n {\n var request = new HttpRequestMessage(HttpMethod.Post, url);\n request.Headers.Add(\"x-api-key\", apiKey);\n request.Headers.Add(\"anthropic-version\", \"2023-06-01\");\n request.Content = new StringContent(json, Encoding.UTF8, \"application/json\");\n\n var response = await _httpClient.SendAsync(request);\n\n if ((int)response.StatusCode != 529)\n return response;\n\n int waitTime = (int)Math.Pow(2, attempt + 1) * 1000; // 2s, 4s, 8s\n await Task.Delay(waitTime);\n }\n\n throw new Exception(\"Claude API is overloaded (529) after multiple retries.\");\n }\n\n #endregion\n }\n public enum AiServicesList\n {\n OpenAi,\n Grok,\n Claude,\n DeepSeek\n }\n}\n"}, "b226e6dd07ab73064c592b8bd906402b6b6b7f42": {"t": 1785760513.8067992, "n": 18117, "h": "5e15b8375b5fb917", "c": "using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing OpenAI.Chat;\nusing System.Text;\nusing System.Text.Json;\n\nnamespace SafeCash.Test.ApiTest.Integration.OpenAi\n{\n public class OpenAiService\n {\n public static string openAiModel = \"gpt-4o-mini\";\n public static string grokAiMode = \"grok-3-mini-fast-latest\";\n #region SystemPrompt\n public enum SystemPromptTypeEnum\n {\n ApiRequest,\n MobileTextInpueRequest,\n MobileSystemPromptMissionTask,\n MobileXyCordinateRequest,\n ImagesCompare,\n AppPackageDetails,\n }\n string mobilePrePrompt = \"You are an automation expert.\\n\" +\n \"Given a mobile XML element dump, return only the XPath of the button or element best matching the target.\\n\\n\" +\n \"Rules:\\n\" +\n \"- Match by content-desc, text, or resource-id.\\n\" +\n \"- If provided, also consider color or visual clues (e.g., \\\"Blue icon\\\") when content-desc, text, or resource-id are insufficient.\\n\" +\n \"- One match → return XPath only. Multiple → return count. No match → return 0.\\n\" +\n \"- Allow partial matches (substring, typo, etc.).\\n\" +\n \"- Recognize semantic and visual terms (e.g., '3 dots', 'plus icon', 'person icon', color descriptions):\\n\" +\n \" • 'plus icon' → content-desc with 'Add'/'Create' or resource-id with 'fab'\\n\" +\n \" • 'search field' → EditText or 'Search'\\n\" +\n \" • 'person icon' → profile/avatar/user\\n\" +\n \" • 'menu button' or '3 dots' → 'More options' or 'Customize and control'\\n\" +\n \" • Color terms (e.g., \\\"Blue icon\\\") → prioritize clickable buttons (ImageButton, Button, ImageView) without text\\n\" +\n \" especially when stated clearly like FAB buttons or visually distinct elements\\n\" +\n \" • Also infer visual roles or icon types from user terms (e.g., 'record icon', 'dialpad icon', '10 dots') even if no exact match in content-desc/text\\n\" +\n \"- Layouts (FrameLayout, LinearLayout) are valid if clickable and labeled.\\n\\n\" +\n \"Examples:\\n\" +\n \"<android.widget.ImageButton content-desc=\\\"5\\\" />\\n\" +\n \"Target: 5\\n\" +\n \"Expected: //android.widget.ImageButton[@content-desc=\\\"5\\\"]\\n\\n\" +\n \"<android.widget.ImageView resource-id=\\\"com.app:id/profile_icon\\\" />\\n\" +\n \"Target: person icon\\n\" +\n \"Expected: //android.widget.ImageView[@resource-id=\\\"com.app:id/profile_icon\\\"]\\n\\n\" +\n \"<android.widget.Button content-desc=\\\"Add city\\\" resource-id=\\\"com.google.android.deskclock:id/fab\\\" />\\n\" +\n \"Target: plus icon\\n\" +\n \"Expected: //android.widget.Button[@content-desc=\\\"Add city\\\"]\\n\\n\" +\n \"<android.widget.ImageButton content-desc=\\\"key pad\\\" resource-id=\\\"com.google.android.dialer:id/dialpad_fab\\\" clickable=\\\"true\\\" />\\n\" +\n \"Target: Blue icon with 10 dots\\n\" +\n \"Expected: //android.widget.ImageButton[@resource-id=\\\"com.google.android.dialer:id/dialpad_fab\\\"]\\n\\n\" +\n \"Important: Return ONLY the raw XPath string (no markdown, no quotes, no formatting).\\n\" +\n \"Now analyze the following:\";\n\n string mobilePrePromptCordinateXy = \"You are an automation expert.\\n\" +\n \"Your task is:\\n\" +\n \"Given:\\n\" +\n \"- A full XML dump of a mobile screen hierarchy.\\n\" +\n \"- The screen width and height.\\n\" +\n \"- A pair of coordinates (X and Y).\\n\\n\" +\n \"Rules:\\n\" +\n \"1. Identify the single UI element in the XML whose bounds ([left,top][right,bottom]) fully contain the given X and Y coordinates.\\n\" +\n \"2. If multiple elements overlap, select the deepest (most nested) element.\\n\" +\n \"3. Return only the XPath expression to locate that specific element.\\n\" +\n \"4. XPath must be precise and use resource-id or content-desc if available.\\n\" +\n \" - Prefer resource-id when present.\\n\" +\n \" - If resource-id is missing, use content-desc.\\n\" +\n \" - If both are missing, use full tag and index path.\\n\" +\n \"5. If no matching element is found, return exactly the number 0 (zero) and nothing else.\\n\" +\n \"6. Never explain, apologize, or add any extra text.\\n\\n\" +\n \"Examples of valid responses:\\n\" +\n \"- //android.widget.ImageButton[@resource-id=\\\"com.example:id/button\\\"]\\n\" +\n \"- //android.widget.TextView[@content-desc=\\\"Submit\\\"]\\n\" +\n \"- 0\\n\\n\" +\n \"Important:\\n\" +\n \"- Screen coordinates are based on the screen size.\\n\" +\n \"- Bounds are given as [left,top][right,bottom] relative to the screen.\\n\" +\n \"- Coordinates on the exact border (equal to left or top but less than right or bottom) are considered inside the bounds.\\n\" +\n \"- Always return only one XPath or 0.\\n\" +\n \"7. Coordinates must be strictly inside the bounds:\\n \" +\n \"- X must be >= left and < right.\\n \" +\n \" - Y must be >= top and < bottom.\\n \" +\n \"- No partial matches are allowed.\\n \" +\n \"- Ignore elements where X and Y are not fully contained inside bounds.\\n\";\n\n string aiSystemPromptMissionTask =\n \"You are a smart navigation agent inside a mobile app.\\n\\n\" +\n \"Your task is to analyze the given Android XML UI hierarchy and guide the user step-by-step to complete their goal.\\n\\n\" +\n \"Input:\\n\" +\n \"1. xmlHierarchy – the current screen's XML.\\n\" +\n \"2. userGoal – a free-text description of what the user wants to do (e.g. 'Search for Hello World').\\n\\n\" +\n \"Process:\\n\" +\n \"- Parse the XML and check if the goal is already complete.\\n\" +\n \"- If the user goal is fully completed (all required inputs/actions are done), return:\\n\" +\n \" { \\\"type\\\": 3 }\\n\" +\n \"- If not, find the next visible UI element that moves the user closer to the goal.\\n\" +\n \"- Prefer buttons like 'Next', 'OK', or 'Continue' unless the goal says otherwise.\\n\" +\n \"- Only suggest actions that match real elements in the XML (no guesses).\\n\" +\n \"- Use exact XPaths based on class, text, content-desc, or resource-id.\\n\" +\n \"- Return only one step at a time – button click or input.\\n\" +\n \"- If stuck, return: { \\\"type\\\": 0 }\\n\\n\" +\n\n \"Output format:\\n\" +\n \"- Click: { \\\"type\\\": 1, \\\"xpath\\\": \\\"...\\\" }\\n\" +\n \"- Input: { \\\"type\\\": 2, \\\"xpath\\\": \\\"...\\\", \\\"value\\\": \\\"...\\\" }\\n\" +\n \"- Done: { \\\"type\\\": 3 }\\n\" +\n \"- No action: { \\\"type\\\": 0 }\\n\\n\" +\n\n \"Important:\\n\" +\n \"- If all required steps are marked as already clicked (via provided locators), return { \\\"type\\\": 3 } and do not continue.\\n\" +\n \"- Never return commentary or code blocks – only a valid JSON object.\\n\" +\n \"- Do not return suggestions for extra UI elements if not part of the userGoal.\\n\";\n\n\n string getAppPackageDetailsSystemPrompt =\n @\"You are an Android expert.\n Your task is to extract the appPackage and appActivity for a specific app name (e.g., 'camera' or 'contacts').\n\n You will receive:\n - A list of installed app packages\n - A target app name\n\n ---\n\n Your job is to:\n 1. Find the app package that best matches the target app name (case-insensitive, partial match is allowed).\n 2. Return a valid JSON in this exact format:\n {\n \"\"appPackage\"\": \"\"<appPackage>\"\",\n \"\"appActivity\"\": \"\"<appActivity>\"\"\n }\n\n ---\n\n Rules:\n - Respond with JSON only. No explanation, no extra text.\n - If multiple packages might match, return the best/most likely one.\n - If no match found, still return the JSON with empty values:\n {\n \"\"appPackage\"\": \"\"\"\",\n \"\"appActivity\"\": \"\"\"\"\n }\n \";\n\n public string GetSystemPrompt(SystemPromptTypeEnum aiRequest)\n {\n string prePrompt;\n switch (aiRequest)\n {\n case SystemPromptTypeEnum.MobileTextInpueRequest:\n prePrompt = mobilePrePrompt;\n break;\n case SystemPromptTypeEnum.MobileSystemPromptMissionTask:\n prePrompt = aiSystemPromptMissionTask;\n break;\n case SystemPromptTypeEnum.MobileXyCordinateRequest:\n prePrompt = mobilePrePromptCordinateXy;\n break;\n case SystemPromptTypeEnum.AppPackageDetails:\n prePrompt = getAppPackageDetailsSystemPrompt;\n break;\n\n default:\n prePrompt = \"You are an AI assistant.\"; // Default fallback\n break;\n }\n return prePrompt;\n }\n #endregion\n\n #region OpenAiServiceRequest\n\n public async Task<string> OpenAiServiceRequest(string userPrompts, SystemPromptTypeEnum systemPrompt)\n {\n string model = openAiModel;\n string prePrompt = GetSystemPrompt(systemPrompt);\n\n string apiKey = Environment.GetEnvironmentVariable(\"OPENAI_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n string apiResponce = \"An error occurred or no response was returned.\";\n //string combinedPrompt = $\"{prePrompt}\\n\\n{userPrompts}\";\n try\n {\n ChatClient client = new ChatClient(model, apiKey);\n\n var openAiRequest = new\n {\n model = model,\n messages = new[]\n {\n new { role = \"system\", content = prePrompt },\n new { role = \"user\", content = userPrompts }\n }\n };\n string jsonBody = JsonConvert.SerializeObject(openAiRequest, Formatting.Indented);\n\n UserChatMessage message = new UserChatMessage(jsonBody);\n ChatCompletion completion = await client.CompleteChatAsync(message);\n\n if (completion?.Content != null && completion.Content.Count > 0)\n {\n apiResponce = completion.Content[0].Text;\n\n }\n else\n {\n apiResponce = \"No valid content found in the response.\";\n }\n Console.WriteLine(\"after ai\");\n\n }\n catch (Exception ex)\n {\n Console.WriteLine(ex.Message);\n }\n\n return apiResponce;\n }\n #endregion\n\n #region Grok ai request\n public async Task<string> GrokRequestService(string userMessage, SystemPromptTypeEnum aiRequest)\n {\n string grokUrl = \"https://api.x.ai/v1/chat/completions\";\n string apiGrokKey = Environment.GetEnvironmentVariable(\"GROK_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n\n using (HttpClient client = new HttpClient())\n {\n string aiPrePromptType = GetSystemPrompt(aiRequest);\n\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiGrokKey}\");\n\n var requestBody = new\n {\n messages = new[]\n {\n new { role = \"system\", content = aiPrePromptType },\n new { role = \"user\", content = userMessage }\n },\n model = grokAiMode,\n stream = false,\n temperature = 0\n };\n\n var jsonContent = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, \"application/json\");\n HttpResponseMessage response = await client.PostAsync(grokUrl, jsonContent);\n\n if (response.IsSuccessStatusCode)\n {\n string jsonResponse = await response.Content.ReadAsStringAsync();\n\n // ✅ Extract only the \"content\" field from the response\n using JsonDocument doc = JsonDocument.Parse(jsonResponse);\n\n if (doc.RootElement.TryGetProperty(\"choices\", out JsonElement choicesArray) &&\n choicesArray.GetArrayLength() > 0 &&\n choicesArray[0].TryGetProperty(\"message\", out JsonElement message) &&\n message.TryGetProperty(\"content\", out JsonElement contentElement))\n {\n return contentElement.GetString() ?? \"No content available.\";\n }\n\n return \"Invalid response format.\";\n }\n else\n {\n return $\"Error: {response.StatusCode} - {response.ReasonPhrase}\";\n }\n }\n }\n #endregion\n\n #region Deep seek ai request\n private readonly HttpClient _httpClient = new HttpClient();\n\n public async Task<string> DeepSeekResponceAi(string userPrompts, SystemPromptTypeEnum aiRequest)\n {\n string apiUrl = \"https://api.deepseek.com/chat/completions\"; // Replace with actual API URL\n string bearerToken = \"sk-5706d7050b8c4bddb967ba236538d89d\"; // Replace with actual token\n string prePrompt = GetSystemPrompt(aiRequest);\n var requestBody = new\n {\n model = \"deepseek-chat\",\n messages = new[]\n {\n new { role = \"system\", content = prePrompt },\n new { role = \"user\", content = userPrompts }\n\n },\n stream = false\n };\n\n string jsonRequest = System.Text.Json.JsonSerializer.Serialize(requestBody);\n var content = new StringContent(jsonRequest, Encoding.UTF8, \"application/json\"); // ✅ \"Content-Type\" set correctly here\n\n var request = new HttpRequestMessage(HttpMethod.Post, apiUrl)\n {\n Content = content\n };\n\n request.Headers.Add(\"Authorization\", $\"Bearer {bearerToken}\"); // ✅ Correct place for Authorization\n // No need to add \"Content-Type\" again here\n\n HttpResponseMessage response = await _httpClient.SendAsync(request);\n string jsonResponse = await response.Content.ReadAsStringAsync();\n\n using JsonDocument doc = JsonDocument.Parse(jsonResponse);\n\n string? contentResponce = doc.RootElement\n .GetProperty(\"choices\")[0]\n .GetProperty(\"message\")\n .GetProperty(\"content\")\n .GetString();\n\n return contentResponce;\n }\n #endregion\n\n #region Claude ais request\n\n public async Task<string> GetClaudeResponse(string userPrompts, SystemPromptTypeEnum aiRequest,\n string apiKey = \"\")\n {\n string prePrompt = GetSystemPrompt(aiRequest);\n if (string.IsNullOrEmpty(apiKey))\n {\n apiKey = Environment.GetEnvironmentVariable(\"CLAUDE_API_KEY\") ?? throw new InvalidOperationException(\"API key is missing from environment variables.\");\n // throw new InvalidOperationException(\"API key is missing. Please provide a valid API key for Claude AI.\");\n }\n string url = \"https://api.anthropic.com/v1/messages\";\n string claudeModel = \"claude-3-5-sonnet-20240620\";\n var requestBody = new\n {\n model = \"claude-3-5-haiku-20241022\",\n max_tokens = 1024,\n system = prePrompt,\n messages = new[]\n {\n new { role = \"user\", content = userPrompts }\n }\n };\n\n var json = JsonConvert.SerializeObject(requestBody);\n\n // Use the retry method with fresh request creation\n var response = await SendWithRetries(json, url, apiKey);\n\n string responseContent = await response.Content.ReadAsStringAsync();\n\n if (!response.IsSuccessStatusCode)\n {\n throw new InvalidOperationException($\"API key is incorrect (Anthropic return http status {response.StatusCode}, {responseContent}). \" +\n $\"Please provide a valid API key for Claude AI.\");\n //return $\"Error: {response.StatusCode}\\n{responseContent}\";\n }\n\n JObject parsed = JObject.Parse(responseContent);\n var text = parsed[\"content\"]?[0]?[\"text\"]?.ToString();\n\n return text ?? \"No text content found.\";\n }\n\n private async Task<HttpResponseMessage> SendWithRetries(string json, string url, string apiKey, int maxRetries = 3)\n {\n for (int attempt = 0; attempt < maxRetries; attempt++)\n {\n var request = new HttpRequestMessage(HttpMethod.Post, url);\n request.Headers.Add(\"x-api-key\", apiKey);\n request.Headers.Add(\"anthropic-version\", \"2023-06-01\");\n request.Content = new StringContent(json, Encoding.UTF8, \"application/json\");\n\n var response = await _httpClient.SendAsync(request);\n\n if ((int)response.StatusCode != 529)\n return response;\n\n int waitTime = (int)Math.Pow(2, attempt + 1) * 1000; // 2s, 4s, 8s\n await Task.Delay(waitTime);\n }\n\n throw new Exception(\"Claude API is overloaded (529) after multiple retries.\");\n }\n\n #endregion\n }\n public enum AiServicesList\n {\n OpenAi,\n Grok,\n Claude,\n DeepSeek\n }\n}\n"}}}} |