Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/androidcsofficial_android-code-studio.json

1 line
82 KiB
JSON

{"repo": "androidcsofficial/android-code-studio", "files": {"core/app/src/main/java/com/tom/rv2ide/artificial/secrets/ApiKey.kt": {"e93e7468e6482ea51ef8e1adad6b7c6de86ae7cd": {"t": 1785745708.0044694, "n": 3658, "h": "5709463f3f8b1279", "c": "/*\n * This file is part of AndroidCodeStudio.\n *\n * AndroidCodeStudio is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * AndroidCodeStudio is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with AndroidCodeStudio. If not, see <https://www.gnu.org/licenses/>.\n*/\npackage com.tom.rv2ide.artificial.secrets\n\nimport com.tom.rv2ide.preferences.internal.prefManager\n\n/*\n * @author Mohammed-baqer-null @ https://github.com/Mohammed-baqer-null\n*/\n\nobject ApiKey {\n \n // Check if AI Agent is enabled\n fun isAIAgentEnabled(): Boolean {\n return prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n \n // Gemini API Key\n fun getGeminiApiKey(): String {\n return prefManager.getString(\"ai_agent_gemini_api_key\", \"\")\n }\n \n fun hasGeminiKey(): Boolean {\n val key = getGeminiApiKey()\n return key.isNotBlank() && key.length > 20\n }\n \n // OpenAI API Key\n fun getOpenAIApiKey(): String {\n return prefManager.getString(\"ai_agent_openai_api_key\", \"\")\n }\n \n fun hasOpenAIKey(): Boolean {\n val key = getOpenAIApiKey()\n return key.isNotBlank() && key.length > 20\n }\n \n // Deepseek API Key\n fun getDeepseekApiKey(): String {\n return prefManager.getString(\"ai_agent_deepseek_api_key\", \"\")\n }\n \n fun hasDeepseekKey(): Boolean {\n val key = getDeepseekApiKey()\n return key.isNotBlank() && key.length > 20\n }\n \n // Anthropic API Key\n fun getAnthropicApiKey(): String {\n return prefManager.getString(\"ai_agent_anthropic_api_key\", \"\")\n }\n \n fun hasAnthropicKey(): Boolean {\n val key = getAnthropicApiKey()\n return key.isNotBlank() && key.length > 20\n }\n \n // Grok API Key\n fun getGrokApiKey(): String {\n return prefManager.getString(\"ai_agent_grok_api_key\", \"\")\n }\n \n fun hasGrokKey(): Boolean {\n val key = getGrokApiKey()\n return key.isNotBlank() && key.length > 20\n }\n \n // Legacy methods for backward compatibility\n @Deprecated(\"Use getGeminiApiKey() instead\", ReplaceWith(\"getGeminiApiKey()\"))\n fun getApiKey(): String {\n return getGeminiApiKey()\n }\n \n // Get list of available providers\n fun getAvailableProviders(): List<String> {\n val providers = mutableListOf<String>()\n if (hasGeminiKey()) providers.add(\"Gemini\")\n if (hasOpenAIKey()) providers.add(\"OpenAI\")\n if (hasDeepseekKey()) providers.add(\"Deepseek\")\n if (hasAnthropicKey()) providers.add(\"Anthropic\")\n if (hasGrokKey()) providers.add(\"Grok\")\n return providers\n }\n \n // Get all API keys as a map\n fun getAllApiKeys(): Map<String, String> {\n return mapOf(\n \"gemini\" to getGeminiApiKey(),\n \"openai\" to getOpenAIApiKey(),\n \"deepseek\" to getDeepseekApiKey(),\n \"anthropic\" to getAnthropicApiKey(),\n \"grok\" to getGrokApiKey()\n ).filterValues { it.isNotBlank() }\n }\n \n // Check if any API key is configured\n fun hasAnyApiKey(): Boolean {\n return hasGeminiKey() || hasOpenAIKey() || hasDeepseekKey() || \n hasAnthropicKey() || hasGrokKey()\n }\n}"}, "91e35fd8e0d94d700c0ed92c349c152cc7aa56b3": {"t": 1785755768.811059, "n": 3658, "h": "5709463f3f8b1279", "c": "/*\n * This file is part of AndroidCodeStudio.\n *\n * AndroidCodeStudio is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * AndroidCodeStudio is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with AndroidCodeStudio. If not, see <https://www.gnu.org/licenses/>.\n*/\npackage com.tom.rv2ide.artificial.secrets\n\nimport com.tom.rv2ide.preferences.internal.prefManager\n\n/*\n * @author Mohammed-baqer-null @ https://github.com/Mohammed-baqer-null\n*/\n\nobject ApiKey {\n \n // Check if AI Agent is enabled\n fun isAIAgentEnabled(): Boolean {\n return prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n \n // Gemini API Key\n fun getGeminiApiKey(): String {\n return prefManager.getString(\"ai_agent_gemini_api_key\", \"\")\n }\n \n fun hasGeminiKey(): Boolean {\n val key = getGeminiApiKey()\n return key.isNotBlank() && key.length > 20\n }\n \n // OpenAI API Key\n fun getOpenAIApiKey(): String {\n return prefManager.getString(\"ai_agent_openai_api_key\", \"\")\n }\n \n fun hasOpenAIKey(): Boolean {\n val key = getOpenAIApiKey()\n return key.isNotBlank() && key.length > 20\n }\n \n // Deepseek API Key\n fun getDeepseekApiKey(): String {\n return prefManager.getString(\"ai_agent_deepseek_api_key\", \"\")\n }\n \n fun hasDeepseekKey(): Boolean {\n val key = getDeepseekApiKey()\n return key.isNotBlank() && key.length > 20\n }\n \n // Anthropic API Key\n fun getAnthropicApiKey(): String {\n return prefManager.getString(\"ai_agent_anthropic_api_key\", \"\")\n }\n \n fun hasAnthropicKey(): Boolean {\n val key = getAnthropicApiKey()\n return key.isNotBlank() && key.length > 20\n }\n \n // Grok API Key\n fun getGrokApiKey(): String {\n return prefManager.getString(\"ai_agent_grok_api_key\", \"\")\n }\n \n fun hasGrokKey(): Boolean {\n val key = getGrokApiKey()\n return key.isNotBlank() && key.length > 20\n }\n \n // Legacy methods for backward compatibility\n @Deprecated(\"Use getGeminiApiKey() instead\", ReplaceWith(\"getGeminiApiKey()\"))\n fun getApiKey(): String {\n return getGeminiApiKey()\n }\n \n // Get list of available providers\n fun getAvailableProviders(): List<String> {\n val providers = mutableListOf<String>()\n if (hasGeminiKey()) providers.add(\"Gemini\")\n if (hasOpenAIKey()) providers.add(\"OpenAI\")\n if (hasDeepseekKey()) providers.add(\"Deepseek\")\n if (hasAnthropicKey()) providers.add(\"Anthropic\")\n if (hasGrokKey()) providers.add(\"Grok\")\n return providers\n }\n \n // Get all API keys as a map\n fun getAllApiKeys(): Map<String, String> {\n return mapOf(\n \"gemini\" to getGeminiApiKey(),\n \"openai\" to getOpenAIApiKey(),\n \"deepseek\" to getDeepseekApiKey(),\n \"anthropic\" to getAnthropicApiKey(),\n \"grok\" to getGrokApiKey()\n ).filterValues { it.isNotBlank() }\n }\n \n // Check if any API key is configured\n fun hasAnyApiKey(): Boolean {\n return hasGeminiKey() || hasOpenAIKey() || hasDeepseekKey() || \n hasAnthropicKey() || hasGrokKey()\n }\n}"}}, "core/app/src/main/java/com/tom/rv2ide/preferences/aiAgentPrefExts.kt": {"e93e7468e6482ea51ef8e1adad6b7c6de86ae7cd": {"t": 1785745708.918741, "n": 12732, "h": "21bde241cdda599b", "c": "/*\n * This file is part of AndroidCodeStudio.\n *\n * AndroidCodeStudio is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * AndroidCodeStudio is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with AndroidCodeStudio. If not, see <https://www.gnu.org/licenses/>.\n */\n\npackage com.tom.rv2ide.preferences\n\nimport android.content.Context\nimport androidx.preference.Preference\nimport com.tom.rv2ide.R\nimport com.tom.rv2ide.preferences.internal.prefManager\nimport com.tom.rv2ide.resources.R.string\nimport kotlinx.parcelize.IgnoredOnParcel\nimport kotlinx.parcelize.Parcelize\n\n/** * @author Mohammed-baqer-null @ https://github.com/Mohammed-baqer-null */\n@Parcelize\nclass AIAgentPreferencesScreen(\n override val key: String = \"idepref_ai_agent\",\n override val title: Int = string.ai_agent_title,\n override val summary: Int? = string.ai_agent_description,\n override val children: List<IPreference> = mutableListOf(),\n) : IPreferenceScreen() {\n\n init {\n addPreference(AIAgentConfig())\n }\n}\n\n@Parcelize\nprivate class AIAgentConfig(\n override val key: String = \"idepref_ai_agent_config\",\n override val title: Int = string.ai_agent_title,\n override val children: List<IPreference> = mutableListOf(),\n) : IPreferenceGroup() {\n\n @IgnoredOnParcel private var geminiApiKeyPref: GeminiApiKey? = null\n @IgnoredOnParcel private var deepseekApiKeyPref: DeepseekApiKey? = null\n @IgnoredOnParcel private var openAIApiKeyPref: OpenAIApiKey? = null\n @IgnoredOnParcel private var anthropicApiKeyPref: AnthropicApiKey? = null\n @IgnoredOnParcel private var grokApiKeyPref: GrokApiKey? = null\n\n init {\n val aiAgentEnabled = AIAgentEnabled { isEnabled -> updateApiKeyPreferencesState(isEnabled) }\n\n geminiApiKeyPref = GeminiApiKey()\n deepseekApiKeyPref = DeepseekApiKey()\n openAIApiKeyPref = OpenAIApiKey()\n anthropicApiKeyPref = AnthropicApiKey()\n grokApiKeyPref = GrokApiKey()\n\n addPreference(aiAgentEnabled)\n addPreference(geminiApiKeyPref!!)\n addPreference(deepseekApiKeyPref!!)\n addPreference(openAIApiKeyPref!!)\n addPreference(anthropicApiKeyPref!!)\n addPreference(grokApiKeyPref!!)\n }\n\n private fun updateApiKeyPreferencesState(isEnabled: Boolean) {\n geminiApiKeyPref?.setEnabled(isEnabled)\n deepseekApiKeyPref?.setEnabled(isEnabled)\n openAIApiKeyPref?.setEnabled(isEnabled)\n anthropicApiKeyPref?.setEnabled(isEnabled)\n grokApiKeyPref?.setEnabled(isEnabled)\n }\n}\n\n\n@Parcelize\nprivate class AIAgentEnabled(\n override val key: String = \"ai_agent_enabled\",\n override val title: Int = R.string.ai_agent_enable,\n @IgnoredOnParcel private val onStateChanged: ((Boolean) -> Unit)? = null,\n) :\n SwitchPreference(\n setValue = { isEnabled ->\n prefManager.putBoolean(\"ai_agent_enabled\", isEnabled)\n onStateChanged?.invoke(isEnabled)\n },\n getValue = { prefManager.getBoolean(\"ai_agent_enabled\", false) },\n ) {\n\n override fun onCreatePreference(context: Context): Preference {\n return super.onCreatePreference(context).apply {\n key = \"ai_agent_enabled\"\n title = context.getString(R.string.ai_agent_enable)\n summary = context.getString(R.string.ai_agent_enable_summary)\n }\n }\n}\n\n\n@Parcelize\nprivate class GrokApiKey(\n override val key: String = \"ai_agent_grok_api_key\",\n override val title: Int = R.string.ai_agent_grok_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_grok_api_key\"\n title = context.getString(R.string.ai_agent_grok_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_grok_api_key\", \"\"))\n editText.hint = \"Enter your xAI Grok API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"Grok API Key\")\n .setMessage(\"Enter your xAI Grok API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_grok_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_grok_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n\n@Parcelize\nprivate class GeminiApiKey(\n override val key: String = \"ai_agent_gemini_api_key\",\n override val title: Int = R.string.ai_agent_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_gemini_api_key\"\n title = context.getString(R.string.ai_agent_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_gemini_api_key\", \"\"))\n editText.hint = \"Enter your Google Gemini API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"Gemini API Key\")\n .setMessage(\"Enter your Google Gemini API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_gemini_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_gemini_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n\n@Parcelize\nprivate class DeepseekApiKey(\n override val key: String = \"ai_agent_deepseek_api_key\",\n override val title: Int = R.string.ai_agent_deepseek_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_deepseek_api_key\"\n title = context.getString(R.string.ai_agent_deepseek_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_deepseek_api_key\", \"\"))\n editText.hint = \"Enter your Deepseek API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"Deepseek API Key\")\n .setMessage(\"Enter your Deepseek API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_deepseek_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_deepseek_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n\n@Parcelize\nprivate class OpenAIApiKey(\n override val key: String = \"ai_agent_openai_api_key\",\n override val title: Int = R.string.ai_agent_openai_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_openai_api_key\"\n title = context.getString(R.string.ai_agent_openai_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_openai_api_key\", \"\"))\n editText.hint = \"Enter your OpenAI API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"OpenAI API Key\")\n .setMessage(\"Enter your OpenAI API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_openai_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_openai_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n\n@Parcelize\nprivate class AnthropicApiKey(\n override val key: String = \"ai_agent_anthropic_api_key\",\n override val title: Int = R.string.ai_agent_anthropic_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_anthropic_api_key\"\n title = context.getString(R.string.ai_agent_anthropic_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_anthropic_api_key\", \"\"))\n editText.hint = \"Enter your Anthropic API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"Anthropic API Key\")\n .setMessage(\"Enter your Anthropic API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_anthropic_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_anthropic_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n"}, "91e35fd8e0d94d700c0ed92c349c152cc7aa56b3": {"t": 1785755770.1005883, "n": 12732, "h": "21bde241cdda599b", "c": "/*\n * This file is part of AndroidCodeStudio.\n *\n * AndroidCodeStudio is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * AndroidCodeStudio is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with AndroidCodeStudio. If not, see <https://www.gnu.org/licenses/>.\n */\n\npackage com.tom.rv2ide.preferences\n\nimport android.content.Context\nimport androidx.preference.Preference\nimport com.tom.rv2ide.R\nimport com.tom.rv2ide.preferences.internal.prefManager\nimport com.tom.rv2ide.resources.R.string\nimport kotlinx.parcelize.IgnoredOnParcel\nimport kotlinx.parcelize.Parcelize\n\n/** * @author Mohammed-baqer-null @ https://github.com/Mohammed-baqer-null */\n@Parcelize\nclass AIAgentPreferencesScreen(\n override val key: String = \"idepref_ai_agent\",\n override val title: Int = string.ai_agent_title,\n override val summary: Int? = string.ai_agent_description,\n override val children: List<IPreference> = mutableListOf(),\n) : IPreferenceScreen() {\n\n init {\n addPreference(AIAgentConfig())\n }\n}\n\n@Parcelize\nprivate class AIAgentConfig(\n override val key: String = \"idepref_ai_agent_config\",\n override val title: Int = string.ai_agent_title,\n override val children: List<IPreference> = mutableListOf(),\n) : IPreferenceGroup() {\n\n @IgnoredOnParcel private var geminiApiKeyPref: GeminiApiKey? = null\n @IgnoredOnParcel private var deepseekApiKeyPref: DeepseekApiKey? = null\n @IgnoredOnParcel private var openAIApiKeyPref: OpenAIApiKey? = null\n @IgnoredOnParcel private var anthropicApiKeyPref: AnthropicApiKey? = null\n @IgnoredOnParcel private var grokApiKeyPref: GrokApiKey? = null\n\n init {\n val aiAgentEnabled = AIAgentEnabled { isEnabled -> updateApiKeyPreferencesState(isEnabled) }\n\n geminiApiKeyPref = GeminiApiKey()\n deepseekApiKeyPref = DeepseekApiKey()\n openAIApiKeyPref = OpenAIApiKey()\n anthropicApiKeyPref = AnthropicApiKey()\n grokApiKeyPref = GrokApiKey()\n\n addPreference(aiAgentEnabled)\n addPreference(geminiApiKeyPref!!)\n addPreference(deepseekApiKeyPref!!)\n addPreference(openAIApiKeyPref!!)\n addPreference(anthropicApiKeyPref!!)\n addPreference(grokApiKeyPref!!)\n }\n\n private fun updateApiKeyPreferencesState(isEnabled: Boolean) {\n geminiApiKeyPref?.setEnabled(isEnabled)\n deepseekApiKeyPref?.setEnabled(isEnabled)\n openAIApiKeyPref?.setEnabled(isEnabled)\n anthropicApiKeyPref?.setEnabled(isEnabled)\n grokApiKeyPref?.setEnabled(isEnabled)\n }\n}\n\n\n@Parcelize\nprivate class AIAgentEnabled(\n override val key: String = \"ai_agent_enabled\",\n override val title: Int = R.string.ai_agent_enable,\n @IgnoredOnParcel private val onStateChanged: ((Boolean) -> Unit)? = null,\n) :\n SwitchPreference(\n setValue = { isEnabled ->\n prefManager.putBoolean(\"ai_agent_enabled\", isEnabled)\n onStateChanged?.invoke(isEnabled)\n },\n getValue = { prefManager.getBoolean(\"ai_agent_enabled\", false) },\n ) {\n\n override fun onCreatePreference(context: Context): Preference {\n return super.onCreatePreference(context).apply {\n key = \"ai_agent_enabled\"\n title = context.getString(R.string.ai_agent_enable)\n summary = context.getString(R.string.ai_agent_enable_summary)\n }\n }\n}\n\n\n@Parcelize\nprivate class GrokApiKey(\n override val key: String = \"ai_agent_grok_api_key\",\n override val title: Int = R.string.ai_agent_grok_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_grok_api_key\"\n title = context.getString(R.string.ai_agent_grok_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_grok_api_key\", \"\"))\n editText.hint = \"Enter your xAI Grok API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"Grok API Key\")\n .setMessage(\"Enter your xAI Grok API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_grok_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_grok_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n\n@Parcelize\nprivate class GeminiApiKey(\n override val key: String = \"ai_agent_gemini_api_key\",\n override val title: Int = R.string.ai_agent_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_gemini_api_key\"\n title = context.getString(R.string.ai_agent_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_gemini_api_key\", \"\"))\n editText.hint = \"Enter your Google Gemini API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"Gemini API Key\")\n .setMessage(\"Enter your Google Gemini API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_gemini_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_gemini_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n\n@Parcelize\nprivate class DeepseekApiKey(\n override val key: String = \"ai_agent_deepseek_api_key\",\n override val title: Int = R.string.ai_agent_deepseek_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_deepseek_api_key\"\n title = context.getString(R.string.ai_agent_deepseek_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_deepseek_api_key\", \"\"))\n editText.hint = \"Enter your Deepseek API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"Deepseek API Key\")\n .setMessage(\"Enter your Deepseek API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_deepseek_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_deepseek_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n\n@Parcelize\nprivate class OpenAIApiKey(\n override val key: String = \"ai_agent_openai_api_key\",\n override val title: Int = R.string.ai_agent_openai_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_openai_api_key\"\n title = context.getString(R.string.ai_agent_openai_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_openai_api_key\", \"\"))\n editText.hint = \"Enter your OpenAI API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"OpenAI API Key\")\n .setMessage(\"Enter your OpenAI API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_openai_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_openai_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n\n@Parcelize\nprivate class AnthropicApiKey(\n override val key: String = \"ai_agent_anthropic_api_key\",\n override val title: Int = R.string.ai_agent_anthropic_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_anthropic_api_key\"\n title = context.getString(R.string.ai_agent_anthropic_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_anthropic_api_key\", \"\"))\n editText.hint = \"Enter your Anthropic API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"Anthropic API Key\")\n .setMessage(\"Enter your Anthropic API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_anthropic_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_anthropic_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n"}, "e6c2188e7b606fde9c1bf11ece76bb0ccdddf462": {"t": 1785755771.361431, "n": 10761, "h": "b068953140e6f2aa", "c": "/*\n * This file is part of AndroidCodeStudio.\n *\n * AndroidCodeStudio is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * AndroidCodeStudio is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with AndroidCodeStudio. If not, see <https://www.gnu.org/licenses/>.\n */\n\npackage com.tom.rv2ide.preferences\n\nimport android.content.Context\nimport androidx.preference.Preference\nimport com.tom.rv2ide.R\nimport com.tom.rv2ide.preferences.internal.prefManager\nimport com.tom.rv2ide.resources.R.string\nimport kotlinx.parcelize.IgnoredOnParcel\nimport kotlinx.parcelize.Parcelize\n\n/** * @author Mohammed-baqer-null @ https://github.com/Mohammed-baqer-null */\n@Parcelize\nclass AIAgentPreferencesScreen(\n override val key: String = \"idepref_ai_agent\",\n override val title: Int = string.ai_agent_title,\n override val summary: Int? = string.ai_agent_description,\n override val children: List<IPreference> = mutableListOf(),\n) : IPreferenceScreen() {\n\n init {\n addPreference(AIAgentConfig())\n }\n}\n\n@Parcelize\nprivate class AIAgentConfig(\n override val key: String = \"idepref_ai_agent_config\",\n override val title: Int = string.ai_agent_title,\n override val children: List<IPreference> = mutableListOf(),\n) : IPreferenceGroup() {\n\n @IgnoredOnParcel private var geminiApiKeyPref: GeminiApiKey? = null\n @IgnoredOnParcel private var deepseekApiKeyPref: DeepseekApiKey? = null\n @IgnoredOnParcel private var openAIApiKeyPref: OpenAIApiKey? = null\n @IgnoredOnParcel private var anthropicApiKeyPref: AnthropicApiKey? = null\n\n init {\n val aiAgentEnabled = AIAgentEnabled { isEnabled -> updateApiKeyPreferencesState(isEnabled) }\n\n geminiApiKeyPref = GeminiApiKey()\n deepseekApiKeyPref = DeepseekApiKey()\n openAIApiKeyPref = OpenAIApiKey()\n anthropicApiKeyPref = AnthropicApiKey()\n\n addPreference(aiAgentEnabled)\n addPreference(geminiApiKeyPref!!)\n addPreference(deepseekApiKeyPref!!)\n addPreference(openAIApiKeyPref!!)\n addPreference(anthropicApiKeyPref!!)\n }\n\n private fun updateApiKeyPreferencesState(isEnabled: Boolean) {\n geminiApiKeyPref?.setEnabled(isEnabled)\n deepseekApiKeyPref?.setEnabled(isEnabled)\n openAIApiKeyPref?.setEnabled(isEnabled)\n anthropicApiKeyPref?.setEnabled(isEnabled)\n }\n}\n\n@Parcelize\nprivate class AIAgentEnabled(\n override val key: String = \"ai_agent_enabled\",\n override val title: Int = R.string.ai_agent_enable,\n @IgnoredOnParcel private val onStateChanged: ((Boolean) -> Unit)? = null,\n) :\n SwitchPreference(\n setValue = { isEnabled ->\n prefManager.putBoolean(\"ai_agent_enabled\", isEnabled)\n onStateChanged?.invoke(isEnabled)\n },\n getValue = { prefManager.getBoolean(\"ai_agent_enabled\", false) },\n ) {\n\n override fun onCreatePreference(context: Context): Preference {\n return super.onCreatePreference(context).apply {\n key = \"ai_agent_enabled\"\n title = context.getString(R.string.ai_agent_enable)\n summary = context.getString(R.string.ai_agent_enable_summary)\n }\n }\n}\n\n@Parcelize\nprivate class GeminiApiKey(\n override val key: String = \"ai_agent_gemini_api_key\",\n override val title: Int = R.string.ai_agent_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_gemini_api_key\"\n title = context.getString(R.string.ai_agent_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_gemini_api_key\", \"\"))\n editText.hint = \"Enter your Google Gemini API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"Gemini API Key\")\n .setMessage(\"Enter your Google Gemini API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_gemini_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_gemini_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n\n@Parcelize\nprivate class DeepseekApiKey(\n override val key: String = \"ai_agent_deepseek_api_key\",\n override val title: Int = R.string.ai_agent_deepseek_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_deepseek_api_key\"\n title = context.getString(R.string.ai_agent_deepseek_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_deepseek_api_key\", \"\"))\n editText.hint = \"Enter your Deepseek API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"Deepseek API Key\")\n .setMessage(\"Enter your Deepseek API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_deepseek_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_deepseek_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n\n@Parcelize\nprivate class OpenAIApiKey(\n override val key: String = \"ai_agent_openai_api_key\",\n override val title: Int = R.string.ai_agent_openai_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_openai_api_key\"\n title = context.getString(R.string.ai_agent_openai_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_openai_api_key\", \"\"))\n editText.hint = \"Enter your OpenAI API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"OpenAI API Key\")\n .setMessage(\"Enter your OpenAI API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_openai_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_openai_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n\n@Parcelize\nprivate class AnthropicApiKey(\n override val key: String = \"ai_agent_anthropic_api_key\",\n override val title: Int = R.string.ai_agent_anthropic_api_key,\n) : BasePreference() {\n\n @IgnoredOnParcel private var preference: Preference? = null\n\n override fun onCreatePreference(context: Context): Preference {\n preference =\n androidx.preference.Preference(context).apply {\n key = \"ai_agent_anthropic_api_key\"\n title = context.getString(R.string.ai_agent_anthropic_api_key)\n summary = getSummaryText()\n isEnabled = prefManager.getBoolean(\"ai_agent_enabled\", false)\n }\n return preference!!\n }\n\n override fun onPreferenceClick(preference: Preference): Boolean {\n val context = preference.context\n\n val editText = android.widget.EditText(context)\n editText.setText(prefManager.getString(\"ai_agent_anthropic_api_key\", \"\"))\n editText.hint = \"Enter your Anthropic API key\"\n\n val dialog =\n com.google.android.material.dialog\n .MaterialAlertDialogBuilder(context)\n .setTitle(\"Anthropic API Key\")\n .setMessage(\"Enter your Anthropic API key\")\n .setView(editText)\n .setPositiveButton(\"Save\") { _, _ ->\n val apiKey = editText.text.toString().trim()\n prefManager.putString(\"ai_agent_anthropic_api_key\", apiKey)\n preference.summary = getSummaryText()\n }\n .setNegativeButton(\"Cancel\", null)\n .create()\n\n dialog.show()\n return true\n }\n\n fun setEnabled(enabled: Boolean) {\n preference?.isEnabled = enabled\n }\n\n private fun getSummaryText(): String {\n val apiKey = prefManager.getString(\"ai_agent_anthropic_api_key\", \"\")\n return if (apiKey.isBlank()) \"Click to set API key\" else \"API Key: ${apiKey.take(8)}...\"\n }\n}\n"}}, "core/app/src/main/java/com/tom/rv2ide/artificial/agents/deepseek/DeepSeek.kt": {"e93e7468e6482ea51ef8e1adad6b7c6de86ae7cd": {"t": 1785745708.949317, "n": 17920, "h": "fbd0caf755f1e6fe", "c": "/*\n * This file is part of AndroidCodeStudio.\n *\n * AndroidCodeStudio is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * AndroidCodeStudio is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with AndroidCodeStudio. If not, see <https://www.gnu.org/licenses/>.\n*/\n\npackage com.tom.rv2ide.artificial.agents.deepseek\n\nimport android.content.Context\nimport com.tom.rv2ide.artificial.agents.AIAgent\nimport com.tom.rv2ide.artificial.agents.AIAgentRegistry\nimport com.tom.rv2ide.artificial.agents.ModificationAttempt\nimport com.tom.rv2ide.artificial.agents.Agents\nimport com.tom.rv2ide.artificial.rules.WritingRules\nimport com.tom.rv2ide.artificial.project.awareness.ProjectTreeResult\nimport com.tom.rv2ide.artificial.file.AIFileWriter\nimport com.tom.rv2ide.artificial.file.FileWriteResult\nimport com.tom.rv2ide.artificial.exceptions.*\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport java.io.File\nimport java.net.HttpURLConnection\nimport java.net.URL\nimport org.json.JSONObject\nimport org.json.JSONArray\n\nclass DeepSeek : AIAgent {\n\n private var apiKey: String? = null\n private val writingRules = WritingRules.Instructions()\n private var projectTreeResult: ProjectTreeResult? = null\n private var fileWriter: AIFileWriter? = null\n private val conversationHistory = mutableListOf<ConversationMessage>()\n private val modificationHistory = mutableListOf<ModificationAttempt>()\n private var currentAttemptCount = 0\n private val maxRetryAttempts = 3\n private var agents: Agents? = null\n private var selectedModel: String = \"deepseek-chat\"\n override val providerId = \"deepseek\"\n override val providerName = \"DeepSeek\"\n\n companion object {\n fun registerAgent() {\n AIAgentRegistry.register(\"deepseek\", object : AIAgentRegistry.AgentFactory {\n override fun create(context: Context): AIAgent {\n return DeepSeek()\n }\n \n override fun hasValidApiKey(): Boolean {\n val apiKey = getApiKey()\n return apiKey != null && apiKey.isNotBlank() && apiKey.length > 20\n }\n \n override fun getApiKey(): String? {\n val prefManager = com.tom.rv2ide.preferences.internal.prefManager\n return prefManager.getString(\"ai_agent_deepseek_api_key\", \"\")?.takeIf { it.isNotBlank() }\n }\n })\n }\n }\n \n override fun initialize(apiKey: String, context: Context) {\n try {\n this.apiKey = apiKey\n agents = Agents(context)\n var selectedModel = agents?.getAgent() ?: \"deepseek-chat\"\n \n if (!agents!!.isValidModelForProvider(selectedModel, \"deepseek\")) {\n selectedModel = \"deepseek-chat\"\n agents?.setAgent(selectedModel)\n agents?.setProvider(\"deepseek\")\n }\n \n this.selectedModel = selectedModel\n } catch (e: Exception) {\n throw e\n }\n }\n\n override fun reinitializeWithNewModel(apiKey: String, context: Context) {\n initialize(apiKey, context)\n }\n\n override fun setContext(context: Context) {\n fileWriter = AIFileWriter(context)\n }\n\n override fun setProjectData(projectTreeResult: ProjectTreeResult) {\n this.projectTreeResult = projectTreeResult\n }\n\n override fun clearConversation() {\n conversationHistory.clear()\n modificationHistory.clear()\n currentAttemptCount = 0\n }\n\n override fun recordModification(filePath: String, oldContent: String?, newContent: String, success: Boolean) {\n modificationHistory.add(\n ModificationAttempt(\n timestamp = System.currentTimeMillis(),\n filePath = filePath,\n previousContent = oldContent,\n newContent = newContent,\n attemptNumber = currentAttemptCount,\n success = success\n )\n )\n }\n\n override fun undoLastModification(): Boolean {\n if (modificationHistory.isEmpty()) return false\n \n val lastMod = modificationHistory.lastOrNull { it.success } ?: return false\n \n if (lastMod.previousContent != null) {\n val result = writeFile(lastMod.filePath, lastMod.previousContent)\n if (result is FileWriteResult.Success) {\n modificationHistory.removeAt(modificationHistory.lastIndexOf(lastMod))\n return true\n }\n } else {\n try {\n File(lastMod.filePath).delete()\n modificationHistory.removeAt(modificationHistory.lastIndexOf(lastMod))\n return true\n } catch (e: Exception) {\n return false\n }\n }\n return false\n }\n\n override fun getModificationHistory(): List<ModificationAttempt> {\n return modificationHistory.toList()\n }\n\n override fun resetAttemptCount() {\n currentAttemptCount = 0\n }\n\n override fun incrementAttemptCount() {\n currentAttemptCount++\n }\n\n override fun getCurrentAttemptCount(): Int = currentAttemptCount\n\n override fun canRetry(): Boolean = currentAttemptCount < maxRetryAttempts\n\n private fun isUserRequestingCorrection(message: String): Boolean {\n val correctionKeywords = listOf(\n \"wrong\", \"not what\", \"mistake\", \"error\", \"incorrect\", \n \"that's not\", \"not right\", \"fix\", \"undo\", \"revert\",\n \"different\", \"try again\", \"not working\"\n )\n return correctionKeywords.any { message.lowercase().contains(it) }\n }\n\n override suspend fun generateCode(\n prompt: String,\n context: String?,\n language: String,\n projectStructure: String?,\n ): Result<String> =\n withContext(Dispatchers.IO) {\n try {\n val key = apiKey\n ?: return@withContext Result.failure(\n IllegalStateException(\"DeepSeek service not initialized\")\n )\n\n val fileContents = readRelevantFiles()\n val needsCorrection = isUserRequestingCorrection(prompt)\n\n val fullPrompt = buildString {\n append(\"=== PROJECT STRUCTURE (THESE ARE THE EXACT PATHS YOU MUST USE) ===\\n\")\n if (projectTreeResult != null) {\n append(projectTreeResult!!.tree)\n append(\"\\n\\n\")\n append(\"CRITICAL: Use ONLY the paths shown above. Do NOT make up fake paths like '/storage/emulated/0/project' or 'com.example.yourproject'.\\n\")\n append(\"CRITICAL: Look at the actual paths above and use those EXACT paths.\\n\\n\")\n }\n \n if (fileContents.isNotEmpty()) {\n append(\"=== CURRENT FILES CONTENT ===\\n\")\n fileContents.forEach { (path, content) ->\n append(\"FILE: $path\\n\")\n append(\"CONTENT:\\n\")\n append(content)\n append(\"\\n\\n\")\n }\n }\n \n if (context != null) {\n append(\"=== ADDITIONAL CONTEXT ===\\n\")\n append(context)\n append(\"\\n\\n\")\n }\n \n if (conversationHistory.isNotEmpty()) {\n append(\"=== CONVERSATION HISTORY ===\\n\")\n conversationHistory.forEach { msg ->\n append(\"${msg.role.uppercase()}: ${msg.content}\\n\\n\")\n }\n }\n\n if (needsCorrection && modificationHistory.isNotEmpty()) {\n append(\"=== CORRECTION REQUIRED ===\\n\")\n append(\"The user indicated the previous modification was WRONG.\\n\")\n append(\"Previous failed attempts:\\n\")\n modificationHistory.takeLast(3).forEach { attempt ->\n append(\"Attempt ${attempt.attemptNumber}: ${attempt.filePath}\\n\")\n append(\"Result: ${if (attempt.success) \"Applied but user rejected\" else \"Failed\"}\\n\\n\")\n }\n append(\"You MUST try a DIFFERENT approach. Do NOT repeat the same solution.\\n\")\n append(\"Analyze what went wrong and provide a better solution.\\n\\n\")\n }\n\n if (currentAttemptCount > 0) {\n append(\"=== RETRY ATTEMPT $currentAttemptCount/$maxRetryAttempts ===\\n\")\n append(\"This is retry attempt number $currentAttemptCount.\\n\")\n append(\"Previous attempts did not satisfy the user.\\n\")\n append(\"Think carefully and provide a different solution.\\n\\n\")\n }\n \n append(\"=== USER REQUEST ===\\n\")\n append(prompt)\n }\n\n val response = callDeepSeekAPI(key, fullPrompt)\n\n if (response.isBlank()) {\n return@withContext Result.failure(Exception(\"Empty response from AI\"))\n }\n\n conversationHistory.add(ConversationMessage(\"user\", prompt))\n conversationHistory.add(ConversationMessage(\"assistant\", response))\n\n if (conversationHistory.size > 20) {\n conversationHistory.removeAt(0)\n conversationHistory.removeAt(0)\n }\n\n Result.success(response)\n } catch (e: Exception) {\n Result.failure(e)\n }\n }\n\n private fun callDeepSeekAPI(apiKey: String, prompt: String): String {\n android.util.Log.d(\"DeepSeek\", \"Starting API call to DeepSeek\")\n \n val url = URL(\"https://api.deepseek.com/chat/completions\")\n val connection = url.openConnection() as HttpURLConnection\n \n try {\n connection.requestMethod = \"POST\"\n connection.setRequestProperty(\"Content-Type\", \"application/json\")\n connection.setRequestProperty(\"Authorization\", \"Bearer $apiKey\")\n connection.doOutput = true\n connection.connectTimeout = 30000\n connection.readTimeout = 30000\n \n val messages = JSONArray()\n \n val systemMessage = JSONObject()\n systemMessage.put(\"role\", \"system\")\n systemMessage.put(\"content\", writingRules.useThis())\n messages.put(systemMessage)\n \n val userMessage = JSONObject()\n userMessage.put(\"role\", \"user\")\n userMessage.put(\"content\", prompt)\n messages.put(userMessage)\n \n val requestBody = JSONObject()\n requestBody.put(\"model\", selectedModel)\n requestBody.put(\"messages\", messages)\n requestBody.put(\"temperature\", 0.7)\n requestBody.put(\"max_tokens\", 4096)\n \n android.util.Log.d(\"DeepSeek\", \"Request body: ${requestBody.toString()}\")\n \n connection.outputStream.use { os ->\n os.write(requestBody.toString().toByteArray())\n }\n \n val responseCode = connection.responseCode\n android.util.Log.d(\"DeepSeek\", \"Response code: $responseCode\")\n \n if (responseCode != HttpURLConnection.HTTP_OK) {\n val errorStream = connection.errorStream?.bufferedReader()?.readText() ?: \"Unknown error\"\n android.util.Log.e(\"DeepSeek\", \"Error response: $errorStream\")\n \n try {\n val errorJson = JSONObject(errorStream)\n val errorObj = errorJson.optJSONObject(\"error\")\n val errorMessage = errorObj?.optString(\"message\") ?: errorStream\n val errorType = errorObj?.optString(\"type\") ?: \"\"\n val errorCode = errorObj?.optString(\"code\") ?: \"\"\n \n android.util.Log.e(\"DeepSeek\", \"Error type: $errorType, code: $errorCode, message: $errorMessage\")\n \n when {\n responseCode == 429 || errorType.contains(\"rate_limit\") || errorCode.contains(\"rate_limit\") -> \n throw RateLimitException(\"DeepSeek rate limit exceeded: $errorMessage\")\n errorType.contains(\"insufficient_quota\") || errorMessage.contains(\"quota\") || errorMessage.contains(\"billing\") -> \n throw QuotaExceededException(\"DeepSeek quota exceeded: $errorMessage\")\n errorType.contains(\"invalid_api_key\") || errorCode.contains(\"invalid_api_key\") -> \n throw InvalidApiKeyException(\"Invalid DeepSeek API key: $errorMessage\")\n responseCode == 401 -> \n throw InvalidApiKeyException(\"DeepSeek authentication failed: $errorMessage\")\n else -> \n throw Exception(\"DeepSeek API error ($responseCode) - Type: $errorType, Code: $errorCode, Message: $errorMessage\")\n }\n } catch (e: RateLimitException) {\n throw e\n } catch (e: QuotaExceededException) {\n throw e\n } catch (e: InvalidApiKeyException) {\n throw e\n } catch (e: Exception) {\n throw Exception(\"DeepSeek API error ($responseCode): $errorStream\")\n }\n }\n \n val responseBody = connection.inputStream.bufferedReader().readText()\n android.util.Log.d(\"DeepSeek\", \"Success response received, length: ${responseBody.length}\")\n \n val jsonResponse = JSONObject(responseBody)\n \n val choices = jsonResponse.getJSONArray(\"choices\")\n if (choices.length() > 0) {\n val firstChoice = choices.getJSONObject(0)\n val message = firstChoice.getJSONObject(\"message\")\n return message.getString(\"content\")\n }\n \n throw Exception(\"No response from DeepSeek API\")\n } catch (e: RateLimitException) {\n android.util.Log.e(\"DeepSeek\", \"Rate limit exception\", e)\n throw e\n } catch (e: QuotaExceededException) {\n android.util.Log.e(\"DeepSeek\", \"Quota exceeded exception\", e)\n throw e\n } catch (e: InvalidApiKeyException) {\n android.util.Log.e(\"DeepSeek\", \"Invalid API key exception\", e)\n throw e\n } catch (e: java.net.SocketTimeoutException) {\n android.util.Log.e(\"DeepSeek\", \"Timeout exception\", e)\n throw Exception(\"DeepSeek request timeout: ${e.message}\")\n } catch (e: java.net.UnknownHostException) {\n android.util.Log.e(\"DeepSeek\", \"Network exception\", e)\n throw Exception(\"Network error - cannot reach DeepSeek: ${e.message}\")\n } catch (e: Exception) {\n android.util.Log.e(\"DeepSeek\", \"General exception\", e)\n throw e\n } finally {\n connection.disconnect()\n }\n }\n\n private fun readRelevantFiles(): Map<String, String> {\n val filesContent = mutableMapOf<String, String>()\n val tree = projectTreeResult?.tree ?: return filesContent\n \n val filePaths = tree.lines().filter { it.isNotBlank() }\n \n filePaths.forEach { filePath ->\n val trimmedPath = filePath.trim()\n val file = File(trimmedPath)\n \n if (file.isFile && \n (trimmedPath.endsWith(\".kt\") || \n trimmedPath.endsWith(\".java\") ||\n trimmedPath.endsWith(\".xml\") ||\n trimmedPath.endsWith(\".gradle\") ||\n trimmedPath.endsWith(\".gradle.kts\")) &&\n !trimmedPath.contains(\"/build/\") && \n !trimmedPath.contains(\"/.gradle/\")) {\n try {\n val content = file.readText()\n filesContent[trimmedPath] = content\n } catch (e: Exception) {\n }\n }\n }\n \n return filesContent\n }\n\n fun readFile(filePath: String): String? {\n return try {\n if (File(filePath).exists()) {\n File(filePath).readText()\n } else {\n projectTreeResult?.readFileContent(File(filePath).name)\n }\n } catch (e: Exception) {\n null\n }\n }\n\n override fun writeFile(filePath: String, content: String): FileWriteResult {\n val writer = fileWriter ?: return FileWriteResult.Error(\"File writer not initialized\")\n return writer.writeFile(filePath, content, createBackup = true)\n }\n\n fun parseAndApplyModifications(response: String, capturedStates: Map<String, String>): List<FileModification> {\n val modifications = mutableListOf<FileModification>()\n val parser = com.tom.rv2ide.artificial.parser.SnippetParser()\n \n if (response.contains(\"FILE_TO_MODIFY:\")) {\n val lines = response.lines()\n var currentFile: String? = null\n val contentBuilder = StringBuilder()\n var inContent = false\n \n for (line in lines) {\n if (line.startsWith(\"FILE_TO_MODIFY:\")) {\n if (currentFile != null && contentBuilder.isNotEmpty()) {\n val rawContent = contentBuilder.toString().trim()\n val cleanedContent = parser.cleanFileContent(rawContent)\n val previousContent = capturedStates[currentFile]\n val writeResult = writeFile(currentFile, cleanedContent)\n \n val success = writeResult is FileWriteResult.Success\n recordModification(currentFile, previousContent, cleanedContent, success)\n \n modifications.add(FileModification(currentFile, cleanedContent, writeResult))\n }\n \n currentFile = line.substringAfter(\"FILE_TO_MODIFY:\").trim()\n contentBuilder.clear()\n inContent = true\n } else if (inContent) {\n contentBuilder.append(line).append(\"\\n\")\n }\n }\n \n if (currentFile != null && contentBuilder.isNotEmpty()) {\n val rawContent = contentBuilder.toString().trim()\n val cleanedContent = parser.cleanFileContent(rawContent)\n val previousContent = capturedStates[currentFile]\n val writeResult = writeFile(currentFile, cleanedContent)\n \n val success = writeResult is FileWriteResult.Success\n recordModification(currentFile, previousContent, cleanedContent, success)\n \n modifications.add(FileModification(currentFile, cleanedContent, writeResult))\n }\n }\n \n return modifications\n }\n\n override fun isInitialized(): Boolean = apiKey != null\n}\n\ndata class FileModification(\n val filePath: String,\n val content: String,\n val writeResult: FileWriteResult\n)\n\ndata class ConversationMessage(\n val role: String,\n val content: String\n)\n"}, "91e35fd8e0d94d700c0ed92c349c152cc7aa56b3": {"t": 1785755769.7529156, "n": 17920, "h": "fbd0caf755f1e6fe", "c": "/*\n * This file is part of AndroidCodeStudio.\n *\n * AndroidCodeStudio is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * AndroidCodeStudio is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with AndroidCodeStudio. If not, see <https://www.gnu.org/licenses/>.\n*/\n\npackage com.tom.rv2ide.artificial.agents.deepseek\n\nimport android.content.Context\nimport com.tom.rv2ide.artificial.agents.AIAgent\nimport com.tom.rv2ide.artificial.agents.AIAgentRegistry\nimport com.tom.rv2ide.artificial.agents.ModificationAttempt\nimport com.tom.rv2ide.artificial.agents.Agents\nimport com.tom.rv2ide.artificial.rules.WritingRules\nimport com.tom.rv2ide.artificial.project.awareness.ProjectTreeResult\nimport com.tom.rv2ide.artificial.file.AIFileWriter\nimport com.tom.rv2ide.artificial.file.FileWriteResult\nimport com.tom.rv2ide.artificial.exceptions.*\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport java.io.File\nimport java.net.HttpURLConnection\nimport java.net.URL\nimport org.json.JSONObject\nimport org.json.JSONArray\n\nclass DeepSeek : AIAgent {\n\n private var apiKey: String? = null\n private val writingRules = WritingRules.Instructions()\n private var projectTreeResult: ProjectTreeResult? = null\n private var fileWriter: AIFileWriter? = null\n private val conversationHistory = mutableListOf<ConversationMessage>()\n private val modificationHistory = mutableListOf<ModificationAttempt>()\n private var currentAttemptCount = 0\n private val maxRetryAttempts = 3\n private var agents: Agents? = null\n private var selectedModel: String = \"deepseek-chat\"\n override val providerId = \"deepseek\"\n override val providerName = \"DeepSeek\"\n\n companion object {\n fun registerAgent() {\n AIAgentRegistry.register(\"deepseek\", object : AIAgentRegistry.AgentFactory {\n override fun create(context: Context): AIAgent {\n return DeepSeek()\n }\n \n override fun hasValidApiKey(): Boolean {\n val apiKey = getApiKey()\n return apiKey != null && apiKey.isNotBlank() && apiKey.length > 20\n }\n \n override fun getApiKey(): String? {\n val prefManager = com.tom.rv2ide.preferences.internal.prefManager\n return prefManager.getString(\"ai_agent_deepseek_api_key\", \"\")?.takeIf { it.isNotBlank() }\n }\n })\n }\n }\n \n override fun initialize(apiKey: String, context: Context) {\n try {\n this.apiKey = apiKey\n agents = Agents(context)\n var selectedModel = agents?.getAgent() ?: \"deepseek-chat\"\n \n if (!agents!!.isValidModelForProvider(selectedModel, \"deepseek\")) {\n selectedModel = \"deepseek-chat\"\n agents?.setAgent(selectedModel)\n agents?.setProvider(\"deepseek\")\n }\n \n this.selectedModel = selectedModel\n } catch (e: Exception) {\n throw e\n }\n }\n\n override fun reinitializeWithNewModel(apiKey: String, context: Context) {\n initialize(apiKey, context)\n }\n\n override fun setContext(context: Context) {\n fileWriter = AIFileWriter(context)\n }\n\n override fun setProjectData(projectTreeResult: ProjectTreeResult) {\n this.projectTreeResult = projectTreeResult\n }\n\n override fun clearConversation() {\n conversationHistory.clear()\n modificationHistory.clear()\n currentAttemptCount = 0\n }\n\n override fun recordModification(filePath: String, oldContent: String?, newContent: String, success: Boolean) {\n modificationHistory.add(\n ModificationAttempt(\n timestamp = System.currentTimeMillis(),\n filePath = filePath,\n previousContent = oldContent,\n newContent = newContent,\n attemptNumber = currentAttemptCount,\n success = success\n )\n )\n }\n\n override fun undoLastModification(): Boolean {\n if (modificationHistory.isEmpty()) return false\n \n val lastMod = modificationHistory.lastOrNull { it.success } ?: return false\n \n if (lastMod.previousContent != null) {\n val result = writeFile(lastMod.filePath, lastMod.previousContent)\n if (result is FileWriteResult.Success) {\n modificationHistory.removeAt(modificationHistory.lastIndexOf(lastMod))\n return true\n }\n } else {\n try {\n File(lastMod.filePath).delete()\n modificationHistory.removeAt(modificationHistory.lastIndexOf(lastMod))\n return true\n } catch (e: Exception) {\n return false\n }\n }\n return false\n }\n\n override fun getModificationHistory(): List<ModificationAttempt> {\n return modificationHistory.toList()\n }\n\n override fun resetAttemptCount() {\n currentAttemptCount = 0\n }\n\n override fun incrementAttemptCount() {\n currentAttemptCount++\n }\n\n override fun getCurrentAttemptCount(): Int = currentAttemptCount\n\n override fun canRetry(): Boolean = currentAttemptCount < maxRetryAttempts\n\n private fun isUserRequestingCorrection(message: String): Boolean {\n val correctionKeywords = listOf(\n \"wrong\", \"not what\", \"mistake\", \"error\", \"incorrect\", \n \"that's not\", \"not right\", \"fix\", \"undo\", \"revert\",\n \"different\", \"try again\", \"not working\"\n )\n return correctionKeywords.any { message.lowercase().contains(it) }\n }\n\n override suspend fun generateCode(\n prompt: String,\n context: String?,\n language: String,\n projectStructure: String?,\n ): Result<String> =\n withContext(Dispatchers.IO) {\n try {\n val key = apiKey\n ?: return@withContext Result.failure(\n IllegalStateException(\"DeepSeek service not initialized\")\n )\n\n val fileContents = readRelevantFiles()\n val needsCorrection = isUserRequestingCorrection(prompt)\n\n val fullPrompt = buildString {\n append(\"=== PROJECT STRUCTURE (THESE ARE THE EXACT PATHS YOU MUST USE) ===\\n\")\n if (projectTreeResult != null) {\n append(projectTreeResult!!.tree)\n append(\"\\n\\n\")\n append(\"CRITICAL: Use ONLY the paths shown above. Do NOT make up fake paths like '/storage/emulated/0/project' or 'com.example.yourproject'.\\n\")\n append(\"CRITICAL: Look at the actual paths above and use those EXACT paths.\\n\\n\")\n }\n \n if (fileContents.isNotEmpty()) {\n append(\"=== CURRENT FILES CONTENT ===\\n\")\n fileContents.forEach { (path, content) ->\n append(\"FILE: $path\\n\")\n append(\"CONTENT:\\n\")\n append(content)\n append(\"\\n\\n\")\n }\n }\n \n if (context != null) {\n append(\"=== ADDITIONAL CONTEXT ===\\n\")\n append(context)\n append(\"\\n\\n\")\n }\n \n if (conversationHistory.isNotEmpty()) {\n append(\"=== CONVERSATION HISTORY ===\\n\")\n conversationHistory.forEach { msg ->\n append(\"${msg.role.uppercase()}: ${msg.content}\\n\\n\")\n }\n }\n\n if (needsCorrection && modificationHistory.isNotEmpty()) {\n append(\"=== CORRECTION REQUIRED ===\\n\")\n append(\"The user indicated the previous modification was WRONG.\\n\")\n append(\"Previous failed attempts:\\n\")\n modificationHistory.takeLast(3).forEach { attempt ->\n append(\"Attempt ${attempt.attemptNumber}: ${attempt.filePath}\\n\")\n append(\"Result: ${if (attempt.success) \"Applied but user rejected\" else \"Failed\"}\\n\\n\")\n }\n append(\"You MUST try a DIFFERENT approach. Do NOT repeat the same solution.\\n\")\n append(\"Analyze what went wrong and provide a better solution.\\n\\n\")\n }\n\n if (currentAttemptCount > 0) {\n append(\"=== RETRY ATTEMPT $currentAttemptCount/$maxRetryAttempts ===\\n\")\n append(\"This is retry attempt number $currentAttemptCount.\\n\")\n append(\"Previous attempts did not satisfy the user.\\n\")\n append(\"Think carefully and provide a different solution.\\n\\n\")\n }\n \n append(\"=== USER REQUEST ===\\n\")\n append(prompt)\n }\n\n val response = callDeepSeekAPI(key, fullPrompt)\n\n if (response.isBlank()) {\n return@withContext Result.failure(Exception(\"Empty response from AI\"))\n }\n\n conversationHistory.add(ConversationMessage(\"user\", prompt))\n conversationHistory.add(ConversationMessage(\"assistant\", response))\n\n if (conversationHistory.size > 20) {\n conversationHistory.removeAt(0)\n conversationHistory.removeAt(0)\n }\n\n Result.success(response)\n } catch (e: Exception) {\n Result.failure(e)\n }\n }\n\n private fun callDeepSeekAPI(apiKey: String, prompt: String): String {\n android.util.Log.d(\"DeepSeek\", \"Starting API call to DeepSeek\")\n \n val url = URL(\"https://api.deepseek.com/chat/completions\")\n val connection = url.openConnection() as HttpURLConnection\n \n try {\n connection.requestMethod = \"POST\"\n connection.setRequestProperty(\"Content-Type\", \"application/json\")\n connection.setRequestProperty(\"Authorization\", \"Bearer $apiKey\")\n connection.doOutput = true\n connection.connectTimeout = 30000\n connection.readTimeout = 30000\n \n val messages = JSONArray()\n \n val systemMessage = JSONObject()\n systemMessage.put(\"role\", \"system\")\n systemMessage.put(\"content\", writingRules.useThis())\n messages.put(systemMessage)\n \n val userMessage = JSONObject()\n userMessage.put(\"role\", \"user\")\n userMessage.put(\"content\", prompt)\n messages.put(userMessage)\n \n val requestBody = JSONObject()\n requestBody.put(\"model\", selectedModel)\n requestBody.put(\"messages\", messages)\n requestBody.put(\"temperature\", 0.7)\n requestBody.put(\"max_tokens\", 4096)\n \n android.util.Log.d(\"DeepSeek\", \"Request body: ${requestBody.toString()}\")\n \n connection.outputStream.use { os ->\n os.write(requestBody.toString().toByteArray())\n }\n \n val responseCode = connection.responseCode\n android.util.Log.d(\"DeepSeek\", \"Response code: $responseCode\")\n \n if (responseCode != HttpURLConnection.HTTP_OK) {\n val errorStream = connection.errorStream?.bufferedReader()?.readText() ?: \"Unknown error\"\n android.util.Log.e(\"DeepSeek\", \"Error response: $errorStream\")\n \n try {\n val errorJson = JSONObject(errorStream)\n val errorObj = errorJson.optJSONObject(\"error\")\n val errorMessage = errorObj?.optString(\"message\") ?: errorStream\n val errorType = errorObj?.optString(\"type\") ?: \"\"\n val errorCode = errorObj?.optString(\"code\") ?: \"\"\n \n android.util.Log.e(\"DeepSeek\", \"Error type: $errorType, code: $errorCode, message: $errorMessage\")\n \n when {\n responseCode == 429 || errorType.contains(\"rate_limit\") || errorCode.contains(\"rate_limit\") -> \n throw RateLimitException(\"DeepSeek rate limit exceeded: $errorMessage\")\n errorType.contains(\"insufficient_quota\") || errorMessage.contains(\"quota\") || errorMessage.contains(\"billing\") -> \n throw QuotaExceededException(\"DeepSeek quota exceeded: $errorMessage\")\n errorType.contains(\"invalid_api_key\") || errorCode.contains(\"invalid_api_key\") -> \n throw InvalidApiKeyException(\"Invalid DeepSeek API key: $errorMessage\")\n responseCode == 401 -> \n throw InvalidApiKeyException(\"DeepSeek authentication failed: $errorMessage\")\n else -> \n throw Exception(\"DeepSeek API error ($responseCode) - Type: $errorType, Code: $errorCode, Message: $errorMessage\")\n }\n } catch (e: RateLimitException) {\n throw e\n } catch (e: QuotaExceededException) {\n throw e\n } catch (e: InvalidApiKeyException) {\n throw e\n } catch (e: Exception) {\n throw Exception(\"DeepSeek API error ($responseCode): $errorStream\")\n }\n }\n \n val responseBody = connection.inputStream.bufferedReader().readText()\n android.util.Log.d(\"DeepSeek\", \"Success response received, length: ${responseBody.length}\")\n \n val jsonResponse = JSONObject(responseBody)\n \n val choices = jsonResponse.getJSONArray(\"choices\")\n if (choices.length() > 0) {\n val firstChoice = choices.getJSONObject(0)\n val message = firstChoice.getJSONObject(\"message\")\n return message.getString(\"content\")\n }\n \n throw Exception(\"No response from DeepSeek API\")\n } catch (e: RateLimitException) {\n android.util.Log.e(\"DeepSeek\", \"Rate limit exception\", e)\n throw e\n } catch (e: QuotaExceededException) {\n android.util.Log.e(\"DeepSeek\", \"Quota exceeded exception\", e)\n throw e\n } catch (e: InvalidApiKeyException) {\n android.util.Log.e(\"DeepSeek\", \"Invalid API key exception\", e)\n throw e\n } catch (e: java.net.SocketTimeoutException) {\n android.util.Log.e(\"DeepSeek\", \"Timeout exception\", e)\n throw Exception(\"DeepSeek request timeout: ${e.message}\")\n } catch (e: java.net.UnknownHostException) {\n android.util.Log.e(\"DeepSeek\", \"Network exception\", e)\n throw Exception(\"Network error - cannot reach DeepSeek: ${e.message}\")\n } catch (e: Exception) {\n android.util.Log.e(\"DeepSeek\", \"General exception\", e)\n throw e\n } finally {\n connection.disconnect()\n }\n }\n\n private fun readRelevantFiles(): Map<String, String> {\n val filesContent = mutableMapOf<String, String>()\n val tree = projectTreeResult?.tree ?: return filesContent\n \n val filePaths = tree.lines().filter { it.isNotBlank() }\n \n filePaths.forEach { filePath ->\n val trimmedPath = filePath.trim()\n val file = File(trimmedPath)\n \n if (file.isFile && \n (trimmedPath.endsWith(\".kt\") || \n trimmedPath.endsWith(\".java\") ||\n trimmedPath.endsWith(\".xml\") ||\n trimmedPath.endsWith(\".gradle\") ||\n trimmedPath.endsWith(\".gradle.kts\")) &&\n !trimmedPath.contains(\"/build/\") && \n !trimmedPath.contains(\"/.gradle/\")) {\n try {\n val content = file.readText()\n filesContent[trimmedPath] = content\n } catch (e: Exception) {\n }\n }\n }\n \n return filesContent\n }\n\n fun readFile(filePath: String): String? {\n return try {\n if (File(filePath).exists()) {\n File(filePath).readText()\n } else {\n projectTreeResult?.readFileContent(File(filePath).name)\n }\n } catch (e: Exception) {\n null\n }\n }\n\n override fun writeFile(filePath: String, content: String): FileWriteResult {\n val writer = fileWriter ?: return FileWriteResult.Error(\"File writer not initialized\")\n return writer.writeFile(filePath, content, createBackup = true)\n }\n\n fun parseAndApplyModifications(response: String, capturedStates: Map<String, String>): List<FileModification> {\n val modifications = mutableListOf<FileModification>()\n val parser = com.tom.rv2ide.artificial.parser.SnippetParser()\n \n if (response.contains(\"FILE_TO_MODIFY:\")) {\n val lines = response.lines()\n var currentFile: String? = null\n val contentBuilder = StringBuilder()\n var inContent = false\n \n for (line in lines) {\n if (line.startsWith(\"FILE_TO_MODIFY:\")) {\n if (currentFile != null && contentBuilder.isNotEmpty()) {\n val rawContent = contentBuilder.toString().trim()\n val cleanedContent = parser.cleanFileContent(rawContent)\n val previousContent = capturedStates[currentFile]\n val writeResult = writeFile(currentFile, cleanedContent)\n \n val success = writeResult is FileWriteResult.Success\n recordModification(currentFile, previousContent, cleanedContent, success)\n \n modifications.add(FileModification(currentFile, cleanedContent, writeResult))\n }\n \n currentFile = line.substringAfter(\"FILE_TO_MODIFY:\").trim()\n contentBuilder.clear()\n inContent = true\n } else if (inContent) {\n contentBuilder.append(line).append(\"\\n\")\n }\n }\n \n if (currentFile != null && contentBuilder.isNotEmpty()) {\n val rawContent = contentBuilder.toString().trim()\n val cleanedContent = parser.cleanFileContent(rawContent)\n val previousContent = capturedStates[currentFile]\n val writeResult = writeFile(currentFile, cleanedContent)\n \n val success = writeResult is FileWriteResult.Success\n recordModification(currentFile, previousContent, cleanedContent, success)\n \n modifications.add(FileModification(currentFile, cleanedContent, writeResult))\n }\n }\n \n return modifications\n }\n\n override fun isInitialized(): Boolean = apiKey != null\n}\n\ndata class FileModification(\n val filePath: String,\n val content: String,\n val writeResult: FileWriteResult\n)\n\ndata class ConversationMessage(\n val role: String,\n val content: String\n)\n"}}}}