cognify-code 0.2.5__py3-none-any.whl → 0.2.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ai_code_assistant/agent/code_agent.py +0 -333
- ai_code_assistant/cli.py +73 -8
- ai_code_assistant/providers/base.py +94 -4
- {cognify_code-0.2.5.dist-info → cognify_code-0.2.6.dist-info}/METADATA +94 -161
- {cognify_code-0.2.5.dist-info → cognify_code-0.2.6.dist-info}/RECORD +9 -9
- {cognify_code-0.2.5.dist-info → cognify_code-0.2.6.dist-info}/WHEEL +0 -0
- {cognify_code-0.2.5.dist-info → cognify_code-0.2.6.dist-info}/entry_points.txt +0 -0
- {cognify_code-0.2.5.dist-info → cognify_code-0.2.6.dist-info}/licenses/LICENSE +0 -0
- {cognify_code-0.2.5.dist-info → cognify_code-0.2.6.dist-info}/top_level.txt +0 -0
|
@@ -808,339 +808,6 @@ Provide a helpful, concise response.
|
|
|
808
808
|
response = AgentResponse(message=full_response, intent=intent)
|
|
809
809
|
yield ("", response)
|
|
810
810
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
def process_stream(self, message: str, use_llm_classification: bool = True) -> Iterator[Tuple[str, Optional[AgentResponse]]]:
|
|
814
|
-
"""
|
|
815
|
-
Process a user message with streaming output.
|
|
816
|
-
|
|
817
|
-
Yields tuples of (chunk, final_response).
|
|
818
|
-
During streaming, final_response is None.
|
|
819
|
-
The last yield will have the complete AgentResponse.
|
|
820
|
-
"""
|
|
821
|
-
# Classify intent (non-streaming, it's fast)
|
|
822
|
-
if use_llm_classification:
|
|
823
|
-
intent = self.intent_classifier.classify_with_llm(message)
|
|
824
|
-
else:
|
|
825
|
-
intent = self.intent_classifier.classify(message)
|
|
826
|
-
|
|
827
|
-
# Route to appropriate streaming handler
|
|
828
|
-
streaming_handlers = {
|
|
829
|
-
IntentType.CODE_GENERATE: self._handle_generate_stream,
|
|
830
|
-
IntentType.CODE_EDIT: self._handle_edit_stream,
|
|
831
|
-
IntentType.CODE_REVIEW: self._handle_review_stream,
|
|
832
|
-
IntentType.CODE_EXPLAIN: self._handle_explain_stream,
|
|
833
|
-
IntentType.CODE_REFACTOR: self._handle_refactor_stream,
|
|
834
|
-
IntentType.GENERAL_CHAT: self._handle_general_chat_stream,
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
handler = streaming_handlers.get(intent.type)
|
|
838
|
-
|
|
839
|
-
if handler:
|
|
840
|
-
yield from handler(message, intent)
|
|
841
|
-
else:
|
|
842
|
-
# Fall back to non-streaming for other intents
|
|
843
|
-
response = self.process(message, use_llm_classification)
|
|
844
|
-
yield (response.message, response)
|
|
845
|
-
|
|
846
|
-
def _handle_explain_stream(self, message: str, intent: Intent) -> Iterator[Tuple[str, Optional[AgentResponse]]]:
|
|
847
|
-
"""Handle code explanation with streaming."""
|
|
848
|
-
if not intent.file_paths:
|
|
849
|
-
response = AgentResponse(
|
|
850
|
-
message="Please specify which file or code you want me to explain.",
|
|
851
|
-
intent=intent,
|
|
852
|
-
)
|
|
853
|
-
yield (response.message, response)
|
|
854
|
-
return
|
|
855
|
-
|
|
856
|
-
file_path = intent.file_paths[0]
|
|
857
|
-
content = self.file_manager.read_file(file_path)
|
|
858
|
-
|
|
859
|
-
if not content:
|
|
860
|
-
response = AgentResponse(
|
|
861
|
-
message=f"Cannot find file: {file_path}",
|
|
862
|
-
intent=intent,
|
|
863
|
-
)
|
|
864
|
-
yield (response.message, response)
|
|
865
|
-
return
|
|
866
|
-
|
|
867
|
-
prompt = f"""Explain the following code in a clear, educational way.
|
|
868
|
-
|
|
869
|
-
## Code ({file_path})
|
|
870
|
-
```
|
|
871
|
-
{content[:5000]}
|
|
872
|
-
```
|
|
873
|
-
|
|
874
|
-
## Instructions
|
|
875
|
-
1. Start with a high-level overview
|
|
876
|
-
2. Explain the main components/functions
|
|
877
|
-
3. Describe the flow of execution
|
|
878
|
-
4. Note any important patterns or techniques used
|
|
879
|
-
5. Keep the explanation concise but thorough
|
|
880
|
-
"""
|
|
881
|
-
|
|
882
|
-
# Stream the explanation
|
|
883
|
-
full_response = f"📖 **Explanation of {file_path}**\n\n"
|
|
884
|
-
yield (f"📖 **Explanation of {file_path}**\n\n", None)
|
|
885
|
-
|
|
886
|
-
for chunk in self.llm.stream(prompt):
|
|
887
|
-
full_response += chunk
|
|
888
|
-
yield (chunk, None)
|
|
889
|
-
|
|
890
|
-
# Final response
|
|
891
|
-
response = AgentResponse(
|
|
892
|
-
message=full_response,
|
|
893
|
-
intent=intent,
|
|
894
|
-
)
|
|
895
|
-
yield ("", response)
|
|
896
|
-
|
|
897
|
-
def _handle_review_stream(self, message: str, intent: Intent) -> Iterator[Tuple[str, Optional[AgentResponse]]]:
|
|
898
|
-
"""Handle code review with streaming."""
|
|
899
|
-
if not intent.file_paths:
|
|
900
|
-
context = self.file_manager.get_project_context()
|
|
901
|
-
py_files = [f.relative_path for f in context.files if f.extension == ".py"][:5]
|
|
902
|
-
|
|
903
|
-
if py_files:
|
|
904
|
-
msg = f"Which file would you like me to review? Found these Python files:\n" + \
|
|
905
|
-
"\n".join(f" • {f}" for f in py_files)
|
|
906
|
-
else:
|
|
907
|
-
msg = "Please specify which file you want me to review."
|
|
908
|
-
|
|
909
|
-
response = AgentResponse(message=msg, intent=intent)
|
|
910
|
-
yield (msg, response)
|
|
911
|
-
return
|
|
912
|
-
|
|
913
|
-
file_path = intent.file_paths[0]
|
|
914
|
-
content = self.file_manager.read_file(file_path)
|
|
915
|
-
|
|
916
|
-
if not content:
|
|
917
|
-
response = AgentResponse(
|
|
918
|
-
message=f"Cannot find file: {file_path}",
|
|
919
|
-
intent=intent,
|
|
920
|
-
)
|
|
921
|
-
yield (response.message, response)
|
|
922
|
-
return
|
|
923
|
-
|
|
924
|
-
yield (f"🔍 **Reviewing {file_path}...**\n\n", None)
|
|
925
|
-
|
|
926
|
-
# Use streaming for the review
|
|
927
|
-
prompt = f"""Review the following code for issues, bugs, and improvements.
|
|
928
|
-
|
|
929
|
-
## Code ({file_path})
|
|
930
|
-
```
|
|
931
|
-
{content[:5000]}
|
|
932
|
-
```
|
|
933
|
-
|
|
934
|
-
## Review Format
|
|
935
|
-
Provide a structured review with:
|
|
936
|
-
1. **Summary** - Brief overview
|
|
937
|
-
2. **Issues** - List any bugs, security issues, or problems
|
|
938
|
-
3. **Suggestions** - Improvements and best practices
|
|
939
|
-
4. **Score** - Rate the code quality (1-10)
|
|
940
|
-
|
|
941
|
-
Be specific and actionable.
|
|
942
|
-
"""
|
|
943
|
-
|
|
944
|
-
full_response = f"🔍 **Reviewing {file_path}...**\n\n"
|
|
945
|
-
|
|
946
|
-
for chunk in self.llm.stream(prompt):
|
|
947
|
-
full_response += chunk
|
|
948
|
-
yield (chunk, None)
|
|
949
|
-
|
|
950
|
-
response = AgentResponse(
|
|
951
|
-
message=full_response,
|
|
952
|
-
intent=intent,
|
|
953
|
-
)
|
|
954
|
-
yield ("", response)
|
|
955
|
-
|
|
956
|
-
def _handle_generate_stream(self, message: str, intent: Intent) -> Iterator[Tuple[str, Optional[AgentResponse]]]:
|
|
957
|
-
"""Handle code generation with streaming."""
|
|
958
|
-
yield ("🔨 **Generating code...**\n\n", None)
|
|
959
|
-
|
|
960
|
-
request = CodeGenerationRequest(
|
|
961
|
-
description=message,
|
|
962
|
-
language=intent.language,
|
|
963
|
-
file_path=intent.file_paths[0] if intent.file_paths else None,
|
|
964
|
-
)
|
|
965
|
-
|
|
966
|
-
# Generate code (this part streams)
|
|
967
|
-
full_code = ""
|
|
968
|
-
prompt = self.code_generator._build_prompt(request)
|
|
969
|
-
|
|
970
|
-
for chunk in self.llm.stream(prompt):
|
|
971
|
-
full_code += chunk
|
|
972
|
-
yield (chunk, None)
|
|
973
|
-
|
|
974
|
-
# Extract and create changeset
|
|
975
|
-
code = self._extract_code(full_code)
|
|
976
|
-
file_path = request.file_path or f"generated.{request.language or 'py'}"
|
|
977
|
-
|
|
978
|
-
generated = GeneratedCode(
|
|
979
|
-
code=code,
|
|
980
|
-
language=request.language or "python",
|
|
981
|
-
file_path=file_path,
|
|
982
|
-
description=request.description,
|
|
983
|
-
)
|
|
984
|
-
|
|
985
|
-
changeset = ChangeSet(description=f"Generate: {message[:50]}...")
|
|
986
|
-
diff = self.diff_engine.create_file_diff(generated.file_path, generated.code)
|
|
987
|
-
changeset.diffs.append(diff)
|
|
988
|
-
|
|
989
|
-
self._pending_changeset = changeset
|
|
990
|
-
|
|
991
|
-
response = AgentResponse(
|
|
992
|
-
message=f"\n\n✅ Code generated for {file_path}",
|
|
993
|
-
intent=intent,
|
|
994
|
-
generated_code=generated,
|
|
995
|
-
changeset=changeset,
|
|
996
|
-
requires_confirmation=True,
|
|
997
|
-
)
|
|
998
|
-
yield ("\n\n✅ Code generated. Apply changes? (yes/no)", response)
|
|
999
|
-
|
|
1000
|
-
def _handle_edit_stream(self, message: str, intent: Intent) -> Iterator[Tuple[str, Optional[AgentResponse]]]:
|
|
1001
|
-
"""Handle code editing with streaming."""
|
|
1002
|
-
if not intent.file_paths:
|
|
1003
|
-
response = AgentResponse(
|
|
1004
|
-
message="Please specify which file you want to edit.",
|
|
1005
|
-
intent=intent,
|
|
1006
|
-
)
|
|
1007
|
-
yield (response.message, response)
|
|
1008
|
-
return
|
|
1009
|
-
|
|
1010
|
-
file_path = intent.file_paths[0]
|
|
1011
|
-
original = self.file_manager.read_file(file_path)
|
|
1012
|
-
|
|
1013
|
-
if not original:
|
|
1014
|
-
response = AgentResponse(
|
|
1015
|
-
message=f"Cannot find file: {file_path}",
|
|
1016
|
-
intent=intent,
|
|
1017
|
-
)
|
|
1018
|
-
yield (response.message, response)
|
|
1019
|
-
return
|
|
1020
|
-
|
|
1021
|
-
yield (f"✏️ **Editing {file_path}...**\n\n", None)
|
|
1022
|
-
|
|
1023
|
-
prompt = f"""Edit the following code according to the user's request.
|
|
1024
|
-
|
|
1025
|
-
## Original Code ({file_path})
|
|
1026
|
-
```
|
|
1027
|
-
{original[:5000]}
|
|
1028
|
-
```
|
|
1029
|
-
|
|
1030
|
-
## User Request
|
|
1031
|
-
{message}
|
|
1032
|
-
|
|
1033
|
-
## Instructions
|
|
1034
|
-
Return the COMPLETE modified file.
|
|
1035
|
-
|
|
1036
|
-
```
|
|
1037
|
-
"""
|
|
1038
|
-
|
|
1039
|
-
full_response = ""
|
|
1040
|
-
for chunk in self.llm.stream(prompt):
|
|
1041
|
-
full_response += chunk
|
|
1042
|
-
yield (chunk, None)
|
|
1043
|
-
|
|
1044
|
-
new_code = self._extract_code(full_response)
|
|
1045
|
-
|
|
1046
|
-
changeset = ChangeSet(description=f"Edit: {message[:50]}...")
|
|
1047
|
-
diff = self.diff_engine.create_diff(original, new_code, file_path)
|
|
1048
|
-
changeset.diffs.append(diff)
|
|
1049
|
-
|
|
1050
|
-
self._pending_changeset = changeset
|
|
1051
|
-
|
|
1052
|
-
response = AgentResponse(
|
|
1053
|
-
message=f"\n\n✅ Edit complete for {file_path}",
|
|
1054
|
-
intent=intent,
|
|
1055
|
-
changeset=changeset,
|
|
1056
|
-
requires_confirmation=True,
|
|
1057
|
-
)
|
|
1058
|
-
yield ("\n\n✅ Edit complete. Apply changes? (yes/no)", response)
|
|
1059
|
-
|
|
1060
|
-
def _handle_refactor_stream(self, message: str, intent: Intent) -> Iterator[Tuple[str, Optional[AgentResponse]]]:
|
|
1061
|
-
"""Handle code refactoring with streaming."""
|
|
1062
|
-
if not intent.file_paths:
|
|
1063
|
-
response = AgentResponse(
|
|
1064
|
-
message="Please specify which file you want to refactor.",
|
|
1065
|
-
intent=intent,
|
|
1066
|
-
)
|
|
1067
|
-
yield (response.message, response)
|
|
1068
|
-
return
|
|
1069
|
-
|
|
1070
|
-
file_path = intent.file_paths[0]
|
|
1071
|
-
original = self.file_manager.read_file(file_path)
|
|
1072
|
-
|
|
1073
|
-
if not original:
|
|
1074
|
-
response = AgentResponse(
|
|
1075
|
-
message=f"Cannot find file: {file_path}",
|
|
1076
|
-
intent=intent,
|
|
1077
|
-
)
|
|
1078
|
-
yield (response.message, response)
|
|
1079
|
-
return
|
|
1080
|
-
|
|
1081
|
-
yield (f"🔄 **Refactoring {file_path}...**\n\n", None)
|
|
1082
|
-
|
|
1083
|
-
prompt = f"""Refactor the following code to improve its quality.
|
|
1084
|
-
|
|
1085
|
-
## Original Code ({file_path})
|
|
1086
|
-
```
|
|
1087
|
-
{original[:5000]}
|
|
1088
|
-
```
|
|
1089
|
-
|
|
1090
|
-
## User Request
|
|
1091
|
-
{message}
|
|
1092
|
-
|
|
1093
|
-
Return the COMPLETE refactored file.
|
|
1094
|
-
|
|
1095
|
-
```
|
|
1096
|
-
"""
|
|
1097
|
-
|
|
1098
|
-
full_response = ""
|
|
1099
|
-
for chunk in self.llm.stream(prompt):
|
|
1100
|
-
full_response += chunk
|
|
1101
|
-
yield (chunk, None)
|
|
1102
|
-
|
|
1103
|
-
new_code = self._extract_code(full_response)
|
|
1104
|
-
|
|
1105
|
-
changeset = ChangeSet(description=f"Refactor: {file_path}")
|
|
1106
|
-
diff = self.diff_engine.create_diff(original, new_code, file_path)
|
|
1107
|
-
changeset.diffs.append(diff)
|
|
1108
|
-
|
|
1109
|
-
self._pending_changeset = changeset
|
|
1110
|
-
|
|
1111
|
-
response = AgentResponse(
|
|
1112
|
-
message=f"\n\n✅ Refactoring complete for {file_path}",
|
|
1113
|
-
intent=intent,
|
|
1114
|
-
changeset=changeset,
|
|
1115
|
-
requires_confirmation=True,
|
|
1116
|
-
)
|
|
1117
|
-
yield ("\n\n✅ Refactoring complete. Apply changes? (yes/no)", response)
|
|
1118
|
-
|
|
1119
|
-
def _handle_general_chat_stream(self, message: str, intent: Intent) -> Iterator[Tuple[str, Optional[AgentResponse]]]:
|
|
1120
|
-
"""Handle general chat with streaming."""
|
|
1121
|
-
context = self.file_manager.get_project_context()
|
|
1122
|
-
|
|
1123
|
-
prompt = f"""You are a helpful coding assistant. Answer the user's question.
|
|
1124
|
-
|
|
1125
|
-
Project context:
|
|
1126
|
-
- Root: {context.root_path.name}
|
|
1127
|
-
- Languages: {', '.join(context.languages)}
|
|
1128
|
-
- Files: {context.total_code_files} code files
|
|
1129
|
-
|
|
1130
|
-
User: {message}
|
|
1131
|
-
|
|
1132
|
-
Provide a helpful, concise response.
|
|
1133
|
-
"""
|
|
1134
|
-
|
|
1135
|
-
full_response = ""
|
|
1136
|
-
for chunk in self.llm.stream(prompt):
|
|
1137
|
-
full_response += chunk
|
|
1138
|
-
yield (chunk, None)
|
|
1139
|
-
|
|
1140
|
-
response = AgentResponse(message=full_response, intent=intent)
|
|
1141
|
-
yield ("", response)
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
811
|
def _extract_code(self, response: str) -> str:
|
|
1145
812
|
"""Extract code from LLM response."""
|
|
1146
813
|
import re
|
ai_code_assistant/cli.py
CHANGED
|
@@ -66,6 +66,59 @@ def get_components(config_path: Optional[Path] = None):
|
|
|
66
66
|
return config, llm
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
def validate_llm_connection(llm: LLMManager, exit_on_failure: bool = True) -> bool:
|
|
70
|
+
"""
|
|
71
|
+
Validate LLM connection before operations.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
llm: The LLM manager instance
|
|
75
|
+
exit_on_failure: If True, exit with error code on failure
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
True if connection is valid, False otherwise
|
|
79
|
+
"""
|
|
80
|
+
try:
|
|
81
|
+
info = llm.get_model_info()
|
|
82
|
+
provider = info.get("provider", "ollama")
|
|
83
|
+
|
|
84
|
+
# Quick validation - don't do full connection check for cloud providers with API keys
|
|
85
|
+
if provider != "ollama" and llm.config.llm.api_key:
|
|
86
|
+
return True
|
|
87
|
+
|
|
88
|
+
# For Ollama, check connection
|
|
89
|
+
if provider == "ollama":
|
|
90
|
+
# Try a lightweight check first
|
|
91
|
+
import socket
|
|
92
|
+
base_url = info.get("base_url", "http://localhost:11434")
|
|
93
|
+
host = base_url.replace("http://", "").replace("https://", "").split(":")[0]
|
|
94
|
+
port = int(base_url.split(":")[-1].split("/")[0]) if ":" in base_url else 11434
|
|
95
|
+
|
|
96
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
97
|
+
sock.settimeout(2)
|
|
98
|
+
result = sock.connect_ex((host, port))
|
|
99
|
+
sock.close()
|
|
100
|
+
|
|
101
|
+
if result != 0:
|
|
102
|
+
console.print(f"\n[red]Error: Cannot connect to Ollama at {base_url}[/red]")
|
|
103
|
+
console.print("\n[yellow]Quick fix:[/yellow]")
|
|
104
|
+
console.print(" 1. Make sure Ollama is installed: [cyan]https://ollama.ai[/cyan]")
|
|
105
|
+
console.print(f" 2. Pull the model: [cyan]ollama pull {info.get('model', 'deepseek-coder:6.7b')}[/cyan]")
|
|
106
|
+
console.print(" 3. Start Ollama: [cyan]ollama serve[/cyan]")
|
|
107
|
+
console.print("\n[dim]Or use a cloud provider: cognify status --help[/dim]")
|
|
108
|
+
|
|
109
|
+
if exit_on_failure:
|
|
110
|
+
sys.exit(1)
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
return True
|
|
114
|
+
|
|
115
|
+
except Exception as e:
|
|
116
|
+
console.print(f"\n[red]Error validating LLM connection: {e}[/red]")
|
|
117
|
+
if exit_on_failure:
|
|
118
|
+
sys.exit(1)
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
|
|
69
122
|
@click.group(invoke_without_command=True)
|
|
70
123
|
@click.version_option(version=__version__, prog_name="cognify")
|
|
71
124
|
@click.option("--config", "-c", type=click.Path(exists=True, path_type=Path), help="Config file path")
|
|
@@ -104,6 +157,7 @@ def review(ctx, files: Tuple[Path, ...], review_type: str, output_format: str,
|
|
|
104
157
|
sys.exit(1)
|
|
105
158
|
|
|
106
159
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
160
|
+
validate_llm_connection(llm)
|
|
107
161
|
analyzer = CodeAnalyzer(config, llm)
|
|
108
162
|
file_handler = FileHandler(config)
|
|
109
163
|
formatter = get_formatter(output_format, config.output.use_colors)
|
|
@@ -178,6 +232,7 @@ def generate(ctx, description: str, mode: str, language: str, name: Optional[str
|
|
|
178
232
|
from rich.panel import Panel
|
|
179
233
|
|
|
180
234
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
235
|
+
validate_llm_connection(llm)
|
|
181
236
|
generator = CodeGenerator(config, llm)
|
|
182
237
|
formatter = get_formatter(output_format, config.output.use_colors)
|
|
183
238
|
|
|
@@ -267,6 +322,7 @@ def generate(ctx, description: str, mode: str, language: str, name: Optional[str
|
|
|
267
322
|
def chat(ctx, context: Tuple[Path, ...], stream: bool):
|
|
268
323
|
"""Start an interactive chat session about code."""
|
|
269
324
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
325
|
+
validate_llm_connection(llm)
|
|
270
326
|
session = ChatSession(config, llm)
|
|
271
327
|
|
|
272
328
|
# Load context files
|
|
@@ -558,6 +614,7 @@ def edit(ctx, file: Path, instruction: str, mode: str, preview: bool,
|
|
|
558
614
|
ai-assist edit config.py "Update the timeout value" -s 10 -e 20
|
|
559
615
|
"""
|
|
560
616
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
617
|
+
validate_llm_connection(llm)
|
|
561
618
|
editor = FileEditor(config, llm)
|
|
562
619
|
|
|
563
620
|
# Determine edit mode
|
|
@@ -647,6 +704,7 @@ def refactor(ctx, instruction: str, files: Tuple[Path, ...], pattern: Optional[s
|
|
|
647
704
|
from ai_code_assistant.utils import FileHandler
|
|
648
705
|
|
|
649
706
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
707
|
+
validate_llm_connection(llm)
|
|
650
708
|
editor = MultiFileEditor(config, llm)
|
|
651
709
|
file_handler = FileHandler(config)
|
|
652
710
|
|
|
@@ -788,6 +846,7 @@ def rename(ctx, old_name: str, new_name: str, symbol_type: str, files: Tuple[Pat
|
|
|
788
846
|
from ai_code_assistant.utils import FileHandler
|
|
789
847
|
|
|
790
848
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
849
|
+
validate_llm_connection(llm)
|
|
791
850
|
editor = MultiFileEditor(config, llm)
|
|
792
851
|
file_handler = FileHandler(config)
|
|
793
852
|
|
|
@@ -1182,6 +1241,7 @@ def git_commit(ctx, message: Optional[str], stage_all: bool, push_after: bool, n
|
|
|
1182
1241
|
from ai_code_assistant.git import GitManager, CommitMessageGenerator
|
|
1183
1242
|
|
|
1184
1243
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
1244
|
+
validate_llm_connection(llm)
|
|
1185
1245
|
|
|
1186
1246
|
try:
|
|
1187
1247
|
git_mgr = GitManager()
|
|
@@ -1315,6 +1375,7 @@ def git_sync(ctx, message: Optional[str], no_confirm: bool):
|
|
|
1315
1375
|
from ai_code_assistant.git import GitManager, CommitMessageGenerator
|
|
1316
1376
|
|
|
1317
1377
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
1378
|
+
validate_llm_connection(llm)
|
|
1318
1379
|
|
|
1319
1380
|
try:
|
|
1320
1381
|
git_mgr = GitManager()
|
|
@@ -1451,9 +1512,10 @@ def agent(ctx, path: Path):
|
|
|
1451
1512
|
from ai_code_assistant.chat import AgentChatSession
|
|
1452
1513
|
from rich.markdown import Markdown
|
|
1453
1514
|
from rich.prompt import Prompt
|
|
1454
|
-
|
|
1515
|
+
|
|
1455
1516
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
1456
|
-
|
|
1517
|
+
validate_llm_connection(llm)
|
|
1518
|
+
|
|
1457
1519
|
# Initialize agent session
|
|
1458
1520
|
session = AgentChatSession(config, llm, path.resolve())
|
|
1459
1521
|
|
|
@@ -1591,10 +1653,11 @@ def agent_review(ctx, file: Path, path: Path, stream: bool):
|
|
|
1591
1653
|
ai-assist agent-review main.py --no-stream
|
|
1592
1654
|
"""
|
|
1593
1655
|
from ai_code_assistant.agent import CodeAgent
|
|
1594
|
-
|
|
1656
|
+
|
|
1595
1657
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
1658
|
+
validate_llm_connection(llm)
|
|
1596
1659
|
agent = CodeAgent(llm, path.resolve())
|
|
1597
|
-
|
|
1660
|
+
|
|
1598
1661
|
console.print(f"\n[bold]Reviewing {file}...[/bold]\n")
|
|
1599
1662
|
|
|
1600
1663
|
if stream:
|
|
@@ -1628,10 +1691,11 @@ def agent_generate(ctx, description: str, file: Optional[Path], language: Option
|
|
|
1628
1691
|
ai-assist agent-generate "hello world" --no-stream
|
|
1629
1692
|
"""
|
|
1630
1693
|
from ai_code_assistant.agent import CodeAgent
|
|
1631
|
-
|
|
1694
|
+
|
|
1632
1695
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
1696
|
+
validate_llm_connection(llm)
|
|
1633
1697
|
agent = CodeAgent(llm, path.resolve())
|
|
1634
|
-
|
|
1698
|
+
|
|
1635
1699
|
# Build the request
|
|
1636
1700
|
request = description
|
|
1637
1701
|
if file:
|
|
@@ -1681,10 +1745,11 @@ def agent_explain(ctx, file: Path, path: Path, stream: bool):
|
|
|
1681
1745
|
ai-assist agent-explain main.py --no-stream
|
|
1682
1746
|
"""
|
|
1683
1747
|
from ai_code_assistant.agent import CodeAgent
|
|
1684
|
-
|
|
1748
|
+
|
|
1685
1749
|
config, llm = get_components(ctx.obj.get("config_path"))
|
|
1750
|
+
validate_llm_connection(llm)
|
|
1686
1751
|
agent = CodeAgent(llm, path.resolve())
|
|
1687
|
-
|
|
1752
|
+
|
|
1688
1753
|
console.print(f"\n[bold]Explaining {file}...[/bold]\n")
|
|
1689
1754
|
|
|
1690
1755
|
if stream:
|
|
@@ -3,10 +3,20 @@
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from enum import Enum
|
|
5
5
|
from typing import Any, Dict, Iterator, List, Optional
|
|
6
|
+
import logging
|
|
6
7
|
|
|
7
8
|
from langchain_core.language_models import BaseChatModel
|
|
8
9
|
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
|
|
9
10
|
from pydantic import BaseModel
|
|
11
|
+
from tenacity import (
|
|
12
|
+
retry,
|
|
13
|
+
stop_after_attempt,
|
|
14
|
+
wait_exponential,
|
|
15
|
+
retry_if_exception_type,
|
|
16
|
+
before_sleep_log,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
10
20
|
|
|
11
21
|
|
|
12
22
|
class ProviderType(str, Enum):
|
|
@@ -39,6 +49,21 @@ class ModelInfo(BaseModel):
|
|
|
39
49
|
is_free: bool = True
|
|
40
50
|
|
|
41
51
|
|
|
52
|
+
def _is_retryable_error(exception: Exception) -> bool:
|
|
53
|
+
"""Check if an exception should trigger a retry."""
|
|
54
|
+
error_msg = str(exception).lower()
|
|
55
|
+
# Rate limits, temporary server issues, timeout errors
|
|
56
|
+
retryable_keywords = [
|
|
57
|
+
"rate limit", "rate_limit", "429",
|
|
58
|
+
"503", "502", "504",
|
|
59
|
+
"timeout", "timed out",
|
|
60
|
+
"temporarily unavailable",
|
|
61
|
+
"server error", "internal error",
|
|
62
|
+
"connection", "network",
|
|
63
|
+
]
|
|
64
|
+
return any(keyword in error_msg for keyword in retryable_keywords)
|
|
65
|
+
|
|
66
|
+
|
|
42
67
|
class BaseProvider(ABC):
|
|
43
68
|
"""Abstract base class for LLM providers."""
|
|
44
69
|
|
|
@@ -52,6 +77,11 @@ class BaseProvider(ABC):
|
|
|
52
77
|
# Available models for this provider
|
|
53
78
|
available_models: List[ModelInfo] = []
|
|
54
79
|
|
|
80
|
+
# Retry settings for cloud providers
|
|
81
|
+
_retry_attempts: int = 3
|
|
82
|
+
_retry_min_wait: int = 2
|
|
83
|
+
_retry_max_wait: int = 30
|
|
84
|
+
|
|
55
85
|
def __init__(self, config: ProviderConfig):
|
|
56
86
|
"""Initialize the provider with configuration."""
|
|
57
87
|
self.config = config
|
|
@@ -74,6 +104,10 @@ class BaseProvider(ABC):
|
|
|
74
104
|
"""Validate the provider configuration. Returns (is_valid, error_message)."""
|
|
75
105
|
pass
|
|
76
106
|
|
|
107
|
+
def _should_retry(self) -> bool:
|
|
108
|
+
"""Check if this provider should use retry logic (cloud providers only)."""
|
|
109
|
+
return self.provider_type != ProviderType.OLLAMA
|
|
110
|
+
|
|
77
111
|
def invoke(self, prompt: str, system_prompt: Optional[str] = None) -> str:
|
|
78
112
|
"""Invoke the LLM with a prompt and optional system message."""
|
|
79
113
|
messages: List[BaseMessage] = []
|
|
@@ -81,8 +115,33 @@ class BaseProvider(ABC):
|
|
|
81
115
|
messages.append(SystemMessage(content=system_prompt))
|
|
82
116
|
messages.append(HumanMessage(content=prompt))
|
|
83
117
|
|
|
84
|
-
|
|
85
|
-
|
|
118
|
+
if self._should_retry():
|
|
119
|
+
return self._invoke_with_retry(messages)
|
|
120
|
+
else:
|
|
121
|
+
response = self.llm.invoke(messages)
|
|
122
|
+
return str(response.content)
|
|
123
|
+
|
|
124
|
+
def _invoke_with_retry(self, messages: List[BaseMessage]) -> str:
|
|
125
|
+
"""Invoke with exponential backoff retry for cloud providers."""
|
|
126
|
+
@retry(
|
|
127
|
+
stop=stop_after_attempt(self._retry_attempts),
|
|
128
|
+
wait=wait_exponential(multiplier=1, min=self._retry_min_wait, max=self._retry_max_wait),
|
|
129
|
+
retry=retry_if_exception_type(Exception),
|
|
130
|
+
before_sleep=before_sleep_log(logger, logging.WARNING),
|
|
131
|
+
reraise=True,
|
|
132
|
+
)
|
|
133
|
+
def _do_invoke():
|
|
134
|
+
try:
|
|
135
|
+
response = self.llm.invoke(messages)
|
|
136
|
+
return str(response.content)
|
|
137
|
+
except Exception as e:
|
|
138
|
+
if _is_retryable_error(e):
|
|
139
|
+
logger.warning(f"Retryable error from {self.provider_type.value}: {e}")
|
|
140
|
+
raise # Will be retried
|
|
141
|
+
else:
|
|
142
|
+
raise # Non-retryable, will not retry
|
|
143
|
+
|
|
144
|
+
return _do_invoke()
|
|
86
145
|
|
|
87
146
|
def stream(self, prompt: str, system_prompt: Optional[str] = None) -> Iterator[str]:
|
|
88
147
|
"""Stream LLM response for real-time output."""
|
|
@@ -91,8 +150,39 @@ class BaseProvider(ABC):
|
|
|
91
150
|
messages.append(SystemMessage(content=system_prompt))
|
|
92
151
|
messages.append(HumanMessage(content=prompt))
|
|
93
152
|
|
|
94
|
-
|
|
95
|
-
yield
|
|
153
|
+
if self._should_retry():
|
|
154
|
+
yield from self._stream_with_retry(messages)
|
|
155
|
+
else:
|
|
156
|
+
for chunk in self.llm.stream(messages):
|
|
157
|
+
yield str(chunk.content)
|
|
158
|
+
|
|
159
|
+
def _stream_with_retry(self, messages: List[BaseMessage]) -> Iterator[str]:
|
|
160
|
+
"""Stream with retry logic for cloud providers."""
|
|
161
|
+
last_exception = None
|
|
162
|
+
|
|
163
|
+
for attempt in range(self._retry_attempts):
|
|
164
|
+
try:
|
|
165
|
+
for chunk in self.llm.stream(messages):
|
|
166
|
+
yield str(chunk.content)
|
|
167
|
+
return # Success, exit
|
|
168
|
+
except Exception as e:
|
|
169
|
+
last_exception = e
|
|
170
|
+
if _is_retryable_error(e) and attempt < self._retry_attempts - 1:
|
|
171
|
+
import time
|
|
172
|
+
wait_time = min(
|
|
173
|
+
self._retry_max_wait,
|
|
174
|
+
self._retry_min_wait * (2 ** attempt)
|
|
175
|
+
)
|
|
176
|
+
logger.warning(
|
|
177
|
+
f"Retryable error from {self.provider_type.value}: {e}. "
|
|
178
|
+
f"Retrying in {wait_time}s (attempt {attempt + 1}/{self._retry_attempts})"
|
|
179
|
+
)
|
|
180
|
+
time.sleep(wait_time)
|
|
181
|
+
else:
|
|
182
|
+
raise
|
|
183
|
+
|
|
184
|
+
if last_exception:
|
|
185
|
+
raise last_exception
|
|
96
186
|
|
|
97
187
|
def check_connection(self) -> bool:
|
|
98
188
|
"""Check if the provider is accessible."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cognify-code
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Your local AI-powered code assistant. Review, generate, search, and refactor code with an intelligent AI agent—all running locally with complete privacy.
|
|
5
5
|
Author-email: Ashok Kumar <akkssy@users.noreply.github.com>
|
|
6
6
|
Maintainer-email: Ashok Kumar <akkssy@users.noreply.github.com>
|
|
@@ -39,6 +39,7 @@ Requires-Dist: rich>=13.0.0
|
|
|
39
39
|
Requires-Dist: pyyaml>=6.0
|
|
40
40
|
Requires-Dist: pydantic>=2.0.0
|
|
41
41
|
Requires-Dist: pydantic-settings>=2.0.0
|
|
42
|
+
Requires-Dist: tenacity>=8.0.0
|
|
42
43
|
Requires-Dist: chromadb>=0.4.0
|
|
43
44
|
Requires-Dist: sentence-transformers>=2.0.0
|
|
44
45
|
Requires-Dist: watchdog>=4.0.0
|
|
@@ -68,11 +69,11 @@ Dynamic: license-file
|
|
|
68
69
|
|
|
69
70
|
<p align="center">
|
|
70
71
|
<a href="#features">Features</a> •
|
|
72
|
+
<a href="#vscode-extension">VSCode Extension</a> •
|
|
71
73
|
<a href="#installation">Installation</a> •
|
|
72
74
|
<a href="#providers">Providers</a> •
|
|
73
75
|
<a href="#usage">Usage</a> •
|
|
74
|
-
<a href="#documentation">Docs</a>
|
|
75
|
-
<a href="#contributing">Contributing</a>
|
|
76
|
+
<a href="#documentation">Docs</a>
|
|
76
77
|
</p>
|
|
77
78
|
|
|
78
79
|
<p align="center">
|
|
@@ -80,11 +81,12 @@ Dynamic: license-file
|
|
|
80
81
|
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License">
|
|
81
82
|
<img src="https://img.shields.io/badge/tests-144%20passed-brightgreen.svg" alt="Tests">
|
|
82
83
|
<img src="https://img.shields.io/badge/providers-6%20supported-purple.svg" alt="6 Providers">
|
|
84
|
+
<img src="https://img.shields.io/badge/VSCode-Extension-blue.svg" alt="VSCode Extension">
|
|
83
85
|
</p>
|
|
84
86
|
|
|
85
87
|
---
|
|
86
88
|
|
|
87
|
-
A powerful CLI tool that brings AI-powered code cognition to your
|
|
89
|
+
A powerful CLI tool and **VSCode extension** that brings AI-powered code cognition to your development workflow. Review code, generate functions, search your codebase semantically, and refactor projects—with support for **multiple LLM providers** including local (Ollama) and cloud options with free tiers.
|
|
88
90
|
|
|
89
91
|
## ✨ Features
|
|
90
92
|
|
|
@@ -95,10 +97,67 @@ A powerful CLI tool that brings AI-powered code cognition to your terminal. Revi
|
|
|
95
97
|
| 🔎 **Semantic Search** | Search your codebase using natural language queries |
|
|
96
98
|
| 📝 **AI File Editing** | Edit files with natural language instructions |
|
|
97
99
|
| 🔄 **Multi-File Refactor** | Refactor across multiple files at once |
|
|
98
|
-
|
|
|
100
|
+
| ✏️ **Symbol Renaming** | Rename functions, classes, variables across your project |
|
|
99
101
|
| 💬 **Interactive Chat** | Chat with AI about your code |
|
|
100
102
|
| 📊 **Codebase Indexing** | Create searchable semantic index with RAG |
|
|
101
103
|
| 🌐 **Multi-Provider** | Support for 6 LLM providers (local & cloud) |
|
|
104
|
+
| 🖥️ **VSCode Extension** | Full IDE integration with sidebar chat |
|
|
105
|
+
|
|
106
|
+
## 🖥️ VSCode Extension
|
|
107
|
+
|
|
108
|
+
<p align="center">
|
|
109
|
+
<img src="vscode-extension/images/icon.png" width="64" alt="Cognify VSCode">
|
|
110
|
+
</p>
|
|
111
|
+
|
|
112
|
+
The Cognify AI VSCode extension brings all the power of Cognify directly into your IDE with a beautiful sidebar chat interface.
|
|
113
|
+
|
|
114
|
+
### Extension Features
|
|
115
|
+
|
|
116
|
+
- ⚛️ **Sidebar Chat Panel** - Chat with AI directly in VSCode sidebar
|
|
117
|
+
- 📎 **Add Context** - Include code from your editor in conversations
|
|
118
|
+
- 🔍 **Code Review** - Review files with inline diagnostics
|
|
119
|
+
- ✨ **Code Generation** - Generate code from descriptions
|
|
120
|
+
- 💡 **Code Explanation** - Get explanations for selected code
|
|
121
|
+
- ✏️ **AI Editing** - Edit code with natural language
|
|
122
|
+
|
|
123
|
+
### Quick Actions
|
|
124
|
+
- 📋 Review current file
|
|
125
|
+
- 💡 Explain selected code
|
|
126
|
+
- ✨ Suggest improvements
|
|
127
|
+
- 🧪 Generate tests
|
|
128
|
+
|
|
129
|
+
### Install VSCode Extension
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Build from source
|
|
133
|
+
cd vscode-extension
|
|
134
|
+
npm install
|
|
135
|
+
npm run package
|
|
136
|
+
|
|
137
|
+
# Install the extension
|
|
138
|
+
code --install-extension cognify-ai-0.2.0.vsix
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Or install via VSCode:
|
|
142
|
+
1. Open VSCode
|
|
143
|
+
2. Go to Extensions sidebar
|
|
144
|
+
3. Click "..." → "Install from VSIX..."
|
|
145
|
+
4. Select `cognify-ai-0.2.0.vsix`
|
|
146
|
+
|
|
147
|
+
### Using the Extension
|
|
148
|
+
|
|
149
|
+
1. Click the ⚛️ Cognify icon in the Activity Bar (left sidebar)
|
|
150
|
+
2. Use quick actions or type your question
|
|
151
|
+
3. Click "📎 Add Context" to include code from your editor
|
|
152
|
+
4. Press Enter or click Send
|
|
153
|
+
|
|
154
|
+
**Keyboard Shortcuts:**
|
|
155
|
+
| Command | Mac | Windows/Linux |
|
|
156
|
+
|---------|-----|---------------|
|
|
157
|
+
| Review File | `Cmd+Shift+R` | `Ctrl+Shift+R` |
|
|
158
|
+
| Generate Code | `Cmd+Shift+G` | `Ctrl+Shift+G` |
|
|
159
|
+
| Explain Code | `Cmd+Shift+E` | `Ctrl+Shift+E` |
|
|
160
|
+
| Open Chat | `Cmd+Shift+C` | `Ctrl+Shift+C` |
|
|
102
161
|
|
|
103
162
|
## 🤖 Supported Providers
|
|
104
163
|
|
|
@@ -153,87 +212,46 @@ pip install -e .
|
|
|
153
212
|
|
|
154
213
|
```bash
|
|
155
214
|
# Check status and available providers
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## 🌐 Provider Management
|
|
161
|
-
|
|
162
|
-
### List Available Providers
|
|
163
|
-
```bash
|
|
164
|
-
ai-assist providers
|
|
215
|
+
cognify status
|
|
216
|
+
cognify providers
|
|
165
217
|
```
|
|
166
|
-
Shows all providers with their models, free tier status, and API key requirements.
|
|
167
|
-
|
|
168
|
-
### Switch Providers
|
|
169
|
-
```bash
|
|
170
|
-
# Switch to Groq (fast cloud inference)
|
|
171
|
-
ai-assist use-provider groq --test
|
|
172
218
|
|
|
173
|
-
|
|
174
|
-
ai-assist use-provider google --model gemini-1.5-pro --test
|
|
175
|
-
|
|
176
|
-
# Use OpenRouter with free DeepSeek R1
|
|
177
|
-
ai-assist use-provider openrouter --model deepseek/deepseek-r1:free --test
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### Test Provider Connection
|
|
181
|
-
```bash
|
|
182
|
-
ai-assist test-provider
|
|
183
|
-
ai-assist test-provider --provider groq --prompt "Hello world"
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
## 📖 Usage
|
|
219
|
+
## 📖 CLI Usage
|
|
187
220
|
|
|
188
221
|
### Code Review
|
|
189
222
|
```bash
|
|
190
|
-
|
|
191
|
-
|
|
223
|
+
cognify review path/to/file.py
|
|
224
|
+
cognify review src/ --format json
|
|
192
225
|
```
|
|
193
226
|
|
|
194
227
|
### Code Generation
|
|
195
228
|
```bash
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
ai-assist generate "unit tests for calculator" --mode test
|
|
229
|
+
cognify generate "binary search function" --language python
|
|
230
|
+
cognify generate "REST API client class" --mode class
|
|
199
231
|
```
|
|
200
232
|
|
|
201
233
|
### Semantic Search
|
|
202
234
|
```bash
|
|
203
235
|
# First, index your codebase
|
|
204
|
-
|
|
236
|
+
cognify index .
|
|
205
237
|
|
|
206
238
|
# Then search
|
|
207
|
-
|
|
208
|
-
ai-assist search "database connection" -k 10
|
|
239
|
+
cognify search "error handling"
|
|
209
240
|
```
|
|
210
241
|
|
|
211
242
|
### File Editing
|
|
212
243
|
```bash
|
|
213
|
-
|
|
214
|
-
ai-assist edit utils.py "add type hints" --backup
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Multi-File Refactoring
|
|
218
|
-
```bash
|
|
219
|
-
ai-assist refactor "add docstrings to all functions" -p "src/**/*.py" --dry-run
|
|
220
|
-
ai-assist refactor "convert print to logging" --pattern "**/*.py" --confirm
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### Symbol Renaming
|
|
224
|
-
```bash
|
|
225
|
-
ai-assist rename old_function new_function --type function --dry-run
|
|
226
|
-
ai-assist rename MyClass BetterClass --type class -p "src/**/*.py"
|
|
244
|
+
cognify edit config.py "add logging to all functions" --preview
|
|
227
245
|
```
|
|
228
246
|
|
|
229
247
|
### Interactive Chat
|
|
230
248
|
```bash
|
|
231
|
-
|
|
249
|
+
cognify chat
|
|
232
250
|
```
|
|
233
251
|
|
|
234
252
|
### All Commands
|
|
235
253
|
```bash
|
|
236
|
-
|
|
254
|
+
cognify --help
|
|
237
255
|
```
|
|
238
256
|
|
|
239
257
|
## ⚙️ Configuration
|
|
@@ -242,32 +260,11 @@ Configuration is managed via `config.yaml`:
|
|
|
242
260
|
|
|
243
261
|
```yaml
|
|
244
262
|
llm:
|
|
245
|
-
provider: "ollama"
|
|
263
|
+
provider: "ollama"
|
|
246
264
|
model: "deepseek-coder:6.7b"
|
|
247
|
-
base_url: "http://localhost:11434"
|
|
265
|
+
base_url: "http://localhost:11434"
|
|
248
266
|
temperature: 0.1
|
|
249
267
|
max_tokens: 4096
|
|
250
|
-
timeout: 120
|
|
251
|
-
|
|
252
|
-
review:
|
|
253
|
-
severity_levels: [critical, warning, suggestion]
|
|
254
|
-
categories: [bugs, security, performance, style]
|
|
255
|
-
|
|
256
|
-
generation:
|
|
257
|
-
include_type_hints: true
|
|
258
|
-
include_docstrings: true
|
|
259
|
-
|
|
260
|
-
retrieval:
|
|
261
|
-
embedding_model: "all-MiniLM-L6-v2"
|
|
262
|
-
chunk_size: 50
|
|
263
|
-
|
|
264
|
-
editor:
|
|
265
|
-
create_backup: true
|
|
266
|
-
show_diff: true
|
|
267
|
-
|
|
268
|
-
refactor:
|
|
269
|
-
max_files: 20
|
|
270
|
-
require_confirmation: true
|
|
271
268
|
```
|
|
272
269
|
|
|
273
270
|
Or use environment variables:
|
|
@@ -281,100 +278,36 @@ export GROQ_API_KEY="your-key"
|
|
|
281
278
|
|
|
282
279
|
```
|
|
283
280
|
cognify-ai/
|
|
284
|
-
├── src/ai_code_assistant/
|
|
285
|
-
│ ├── cli.py
|
|
286
|
-
│ ├──
|
|
287
|
-
│ ├──
|
|
288
|
-
│ ├──
|
|
289
|
-
│
|
|
290
|
-
│
|
|
291
|
-
│
|
|
292
|
-
|
|
293
|
-
│
|
|
294
|
-
│
|
|
295
|
-
│
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
│ ├── retrieval/ # Semantic search & indexing (RAG)
|
|
300
|
-
│ ├── editor/ # AI file editing
|
|
301
|
-
│ ├── refactor/ # Multi-file refactoring
|
|
302
|
-
│ ├── chat/ # Interactive chat
|
|
303
|
-
│ └── utils/ # Utilities & formatters
|
|
304
|
-
├── tests/ # 144 unit tests
|
|
305
|
-
├── docs/ # Documentation
|
|
306
|
-
├── config.yaml # Configuration
|
|
307
|
-
└── pyproject.toml # Dependencies
|
|
281
|
+
├── src/ai_code_assistant/ # Core Python package
|
|
282
|
+
│ ├── cli.py # Command-line interface
|
|
283
|
+
│ ├── providers/ # LLM providers
|
|
284
|
+
│ ├── reviewer/ # Code review
|
|
285
|
+
│ ├── generator/ # Code generation
|
|
286
|
+
│ ├── retrieval/ # Semantic search
|
|
287
|
+
│ ├── editor/ # AI file editing
|
|
288
|
+
│ └── chat/ # Interactive chat
|
|
289
|
+
├── vscode-extension/ # VSCode Extension
|
|
290
|
+
│ ├── src/ # Extension source
|
|
291
|
+
│ ├── images/ # Icons
|
|
292
|
+
│ └── package.json # Extension manifest
|
|
293
|
+
├── tests/ # Unit tests
|
|
294
|
+
├── docs/ # Documentation
|
|
295
|
+
└── config.yaml # Configuration
|
|
308
296
|
```
|
|
309
297
|
|
|
310
298
|
## 🧪 Testing
|
|
311
299
|
|
|
312
300
|
```bash
|
|
313
|
-
# Run all tests
|
|
314
301
|
PYTHONPATH=src pytest tests/ -v
|
|
315
|
-
|
|
316
|
-
# Run with coverage
|
|
317
|
-
PYTHONPATH=src pytest tests/ --cov=ai_code_assistant
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
## 🛠️ Tech Stack
|
|
321
|
-
|
|
322
|
-
| Component | Technology |
|
|
323
|
-
|-----------|------------|
|
|
324
|
-
| LLM Framework | LangChain |
|
|
325
|
-
| Local LLM | Ollama |
|
|
326
|
-
| Cloud LLMs | Google, Groq, OpenRouter, OpenAI |
|
|
327
|
-
| Vector Database | ChromaDB |
|
|
328
|
-
| Embeddings | Sentence Transformers |
|
|
329
|
-
| CLI | Click + Rich |
|
|
330
|
-
| Config | Pydantic |
|
|
331
|
-
| Testing | Pytest |
|
|
332
|
-
|
|
333
|
-
## 🐛 Troubleshooting
|
|
334
|
-
|
|
335
|
-
**"Connection refused" error (Ollama)**
|
|
336
|
-
```bash
|
|
337
|
-
ollama serve # Make sure Ollama is running
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
**API Key errors**
|
|
341
|
-
```bash
|
|
342
|
-
ai-assist providers # Check which API keys are set
|
|
343
|
-
export GROQ_API_KEY="your-key" # Set the appropriate key
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
**Test provider connection**
|
|
347
|
-
```bash
|
|
348
|
-
ai-assist test-provider --provider groq
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
**Import errors**
|
|
352
|
-
```bash
|
|
353
|
-
pip install -e ".[dev]"
|
|
354
302
|
```
|
|
355
303
|
|
|
356
304
|
## 🤝 Contributing
|
|
357
305
|
|
|
358
306
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
359
307
|
|
|
360
|
-
1. Fork the repository
|
|
361
|
-
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
362
|
-
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
363
|
-
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
364
|
-
5. Open a Pull Request
|
|
365
|
-
|
|
366
308
|
## 📄 License
|
|
367
309
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
## 🙏 Acknowledgments
|
|
371
|
-
|
|
372
|
-
- [Ollama](https://ollama.ai) - Local LLM runtime
|
|
373
|
-
- [LangChain](https://langchain.com) - LLM framework
|
|
374
|
-
- [Google AI Studio](https://aistudio.google.com) - Gemini models
|
|
375
|
-
- [Groq](https://groq.com) - Fast inference
|
|
376
|
-
- [OpenRouter](https://openrouter.ai) - Multi-provider access
|
|
377
|
-
- [ChromaDB](https://www.trychroma.com) - Vector database
|
|
310
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
378
311
|
|
|
379
312
|
---
|
|
380
313
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
ai_code_assistant/__init__.py,sha256=XnpG4h-2gW3cXseFvqQT_-XyOmVJtikVMrHUnmy8XKI,409
|
|
2
|
-
ai_code_assistant/cli.py,sha256=
|
|
2
|
+
ai_code_assistant/cli.py,sha256=vrz1zt8JbNXflXVpcSr1ZF_SphBrAPTwDcn-qGR5vfU,67912
|
|
3
3
|
ai_code_assistant/config.py,sha256=6sAufexwzfCu2JNWvt9KevS9k_gMcjj1TAnwuaO1ZFw,4727
|
|
4
4
|
ai_code_assistant/llm.py,sha256=DfcWJf6zEAUsPSEZLdEmb9o6BQNf1Ja88nswjpy6cOw,4209
|
|
5
5
|
ai_code_assistant/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
ai_code_assistant/agent/__init__.py,sha256=BcVe4Ebopv_J01ApnRl05oN5yOet5mEefBrQmdPsUj0,1284
|
|
7
|
-
ai_code_assistant/agent/code_agent.py,sha256
|
|
7
|
+
ai_code_assistant/agent/code_agent.py,sha256=lykEvcbGl9V4RdKp2sCrP8-jlL-C7mIqvcAwpXHjcSY,28518
|
|
8
8
|
ai_code_assistant/agent/code_generator.py,sha256=rAaziRU-mJ5NooERjR_Cd6_hwO0kuULw3Sp8Ca9kR48,13138
|
|
9
9
|
ai_code_assistant/agent/code_reviewer.py,sha256=YiM7lRJhoN-vBnQb29jF-5nmE9ppL-OJffvx4ocTHEU,12066
|
|
10
10
|
ai_code_assistant/agent/diff_engine.py,sha256=A5jszowc5VmWbdidpIW_QhswG_Hats3FYuemP8VoYv4,11018
|
|
@@ -27,7 +27,7 @@ ai_code_assistant/git/__init__.py,sha256=YgqmzneAnZyRrbazMqGoFSPIk5Yf5OTm2LXPbkQ
|
|
|
27
27
|
ai_code_assistant/git/commit_generator.py,sha256=CzDH5ZPqEaXyPznBg8FgTz8wbV4adALUQD__kl8au6o,4135
|
|
28
28
|
ai_code_assistant/git/manager.py,sha256=BYeYSz3yPpeJJESy2Zmu4MKEvJ5YAtw3HAmU6uba3nM,6815
|
|
29
29
|
ai_code_assistant/providers/__init__.py,sha256=T8eLHOcjWvqNxLsD8uLmU2H1mJbGbZgUrUcrrVRcqPs,832
|
|
30
|
-
ai_code_assistant/providers/base.py,sha256=
|
|
30
|
+
ai_code_assistant/providers/base.py,sha256=Ep0ZXT2u3_nGUKItAMjf4fxDh2kDRT5CXaZn9P1r6Ys,7279
|
|
31
31
|
ai_code_assistant/providers/cerebras.py,sha256=PfjfFtkFOip5OquyOnxlSQowpy8uPWNRLA6y4m-iYio,3098
|
|
32
32
|
ai_code_assistant/providers/factory.py,sha256=U2zH3HFDGhed2nPRpTyDqG4JcFNHvTvxw25NER2NEi0,4579
|
|
33
33
|
ai_code_assistant/providers/google.py,sha256=nEHsAUiBhV9TjtJEwxkMWydtnWiouVtl_2MrcU8GQNI,3344
|
|
@@ -50,9 +50,9 @@ ai_code_assistant/reviewer/prompts.py,sha256=9RrHEBttS5ngxY2BNsUvqGC6-cTxco-kDPb
|
|
|
50
50
|
ai_code_assistant/utils/__init__.py,sha256=3HO-1Bj4VvUtM7W1C3MKR4DzQ9Xc875QKSHHkHwuqVs,368
|
|
51
51
|
ai_code_assistant/utils/file_handler.py,sha256=jPxvtI5dJxkpPjELgRJ11WXamtyKKmZANQ1fcfMVtiU,5239
|
|
52
52
|
ai_code_assistant/utils/formatters.py,sha256=5El9ew9HS6JLBucBUxxcw4fO5nLpOucgNJrJj2NC3zw,8945
|
|
53
|
-
cognify_code-0.2.
|
|
54
|
-
cognify_code-0.2.
|
|
55
|
-
cognify_code-0.2.
|
|
56
|
-
cognify_code-0.2.
|
|
57
|
-
cognify_code-0.2.
|
|
58
|
-
cognify_code-0.2.
|
|
53
|
+
cognify_code-0.2.6.dist-info/licenses/LICENSE,sha256=5yu_kWq2bK-XKhWo79Eykdg4Qf3O8V2Ys7cpOO7GyyE,1063
|
|
54
|
+
cognify_code-0.2.6.dist-info/METADATA,sha256=JD70dP-OBqiN8_VPo0T1qp-Ifw1OsQLesAD4sruE7vk,10189
|
|
55
|
+
cognify_code-0.2.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
56
|
+
cognify_code-0.2.6.dist-info/entry_points.txt,sha256=MrBnnWPHZVozqqKyTlnJO63YN2kE5yPWKlr2nnRFRks,94
|
|
57
|
+
cognify_code-0.2.6.dist-info/top_level.txt,sha256=dD_r1x-oX0s1uspYY72kig4jfIsjh3oDKwOBCMYXqpo,18
|
|
58
|
+
cognify_code-0.2.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|