Skip to content

Commit

Permalink
Switch package manager to uv and update documentation. (#6)
Browse files Browse the repository at this point in the history
Modified `.gitignore` to include `/arkavo-production/` and removed unnecessary GitLab directory creation step in `README.md`. Added `uv.lock` file to the repository, illustrating the switch from `pip` to `uv` for package management, ensuring consistency in package installations. Updated `README.md` to incorporate `uv` commands and added a prerequisites section with the `uv` installation reference.
arkavo-com authored Dec 5, 2024
1 parent 359887e commit bbcd931
Showing 6 changed files with 1,817 additions and 36 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -226,3 +226,4 @@ cython_debug/
/.idea/
.DS_Store
/gitlab-ce/
/arkavo-production/
45 changes: 35 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# DevSecOps
DevSecOps LangGraph

## Prerequisites

- `uv` https://docs.astral.sh/uv/getting-started/installation/

## Setup

### .env
@@ -23,20 +27,20 @@ ollama install deepseek-coder-v2
### LangGraph

```shell
pip install langgraph langsmith langchain_anthropic
uv add langgraph langsmith langchain_anthropic
```

### Tools

```shell
pip install langchain_community duckduckgo-search langchain-ollama python-gitlab docker
uv add langchain_community duckduckgo-search langchain-ollama python-gitlab docker
```

### Patch

```shell
pip install urllib3==1.26.5
pip install langgraph==0.2.50
uv add urllib3==1.26.5
uv add langgraph==0.2.50
```

# GitLab CE Docker Setup
@@ -57,12 +61,6 @@ This guide helps you set up GitLab Community Edition using Docker Compose on Col
colima start --cpu 4 --memory 8 --disk 50
```

2. Create a new directory for GitLab:
```bash
mkdir gitlab-ce
cd gitlab-ce
```

3. Add GitLab hostname to your hosts file:
```bash
sudo echo "127.0.0.1 gitlab.localhost" >> /etc/hosts
@@ -129,3 +127,30 @@ For persistent permission issues:
```bash
sudo chown -R 998:998 gitlab/
```


Graph

```mermaid
graph TD
%% Current System Structure
START((START))
haiku[haiku]
llama[llama]
deepseek[deepseek]
tools[tools]
gitlab[gitlab]
secrets[secrets]
END((END))
%% Styling
classDef default fill:#bbf,stroke:#333,stroke-width:1px;
classDef router fill:#f9f,stroke:#333,stroke-width:2px;
classDef eend fill:#f96,stroke:#333,stroke-width:2px;
classDef sstart fill:#9f9,stroke:#333,stroke-width:2px;
class START sstart;
class END eend;
class haiku,llama,deepseek,tools,gitlab,secrets default;
```
72 changes: 52 additions & 20 deletions main.py
Original file line number Diff line number Diff line change
@@ -53,7 +53,7 @@ def create_agent_executor(llm: BaseLanguageModel, toollist: List[Tool], system_p
agent=agent,
tools=toollist,
handle_parsing_errors=True,
max_iterations=3,
max_iterations=1,
verbose=True
)

@@ -321,6 +321,7 @@ def process_state(self, state: State) -> dict:
print(error_msg)
return {"messages": [AIMessage(content=error_msg)]}


class ChatNode:
"""Node for handling chat queries with tools."""

@@ -331,18 +332,40 @@ def process_state(self, state: State) -> dict:
"""Process chat queries using the agent executor."""
try:
last_message = state["messages"][-1]
if isinstance(last_message, tuple):
content = last_message[1]
else:
content = last_message.content
content = last_message.content if hasattr(last_message, 'content') else str(last_message)

response = self.agent.invoke({"input": content})
return {"messages": [AIMessage(content=response["output"])]}
output = response["output"]

# Check for definitive system responses
if output.startswith("SYSTEM STATUS:") or output.startswith("SYSTEM ERROR:"):
# Create a more informative response
if "No Docker containers found" in output:
message = ("No Docker containers are currently running or exist. To fix this:\n"
"1. Check if Docker daemon is running\n"
"2. Try starting some containers\n"
"3. Run 'docker ps -a' manually to verify")
else:
message = output

return {
"messages": [AIMessage(content=message)],
"query_type": QueryType.END.value # Force end of processing
}

return {"messages": [AIMessage(content=output)]}

except Exception as ex:
print(f"Error in ChatNode processing: {str(ex)}")
return {"messages": [AIMessage(
content="I encountered an error processing your request. Could you please rephrase or try again?")]}
return {
"messages": [AIMessage(
content="I encountered an error processing your request. Could you please rephrase or try again?")],
"query_type": QueryType.END.value
}

def new_function():
"""Placeholder for a new function."""
pass

def stream_graph_updates(u_input: str):
"""Stream graph updates with proper checkpointing configuration."""
@@ -362,13 +385,13 @@ def stream_graph_updates(u_input: str):
}
}
# print(f"config {config}")
print("Starting graph stream...")
# print("Starting graph stream...")
for event in graph.stream(initial_state, config=config):
print(f"Processing event: {event}")
# print(f"Processing event: {event}")
for value in event.values():
if "messages" in value:
message = value["messages"][-1]
print(f"Message type: {type(message)}")
# print(f"Message type: {type(message)}")
if isinstance(message, (HumanMessage, SystemMessage, AIMessage)):
print("Assistant:", message.content)
elif isinstance(message, tuple):
@@ -415,10 +438,10 @@ def tool_function(instructions: str) -> str:

def create_node_with_logging(node_name, node):
def logged_process(state):
print(f"Processing in {node_name} node")
# print(f"Processing in {node_name} node")
try:
result = node.process_state(state)
print(f"{node_name} processing result: {result}")
# print(f"{node_name} processing result: {result}")
return result
except Exception as e:
print(f"Error in {node_name} node: {str(e)}")
@@ -543,13 +566,22 @@ def logged_process(state):

# Create agent executors with specific prompts
docker_prompt = """When handling Docker-related queries:
1. For listing containers, use the docker_ps tool
2. For analyzing logs, use the docker_log_analysis tool with parameters:
- container_name: Name of the container
- time_range_minutes: How far back to look (default: 60)
- filters: Any specific terms to filter for
- max_lines: Maximum number of lines to analyze (default: 1000)
3. Provide clear explanations of the results"""
1. Pay attention to response prefixes:
- "SYSTEM STATUS:" indicates a definitive system state - do not retry
- "SYSTEM ERROR:" indicates a technical error - do not retry
2. For container listing:
- When you see "No Docker containers found" - accept this as the final state
- Explain to the user what this means and what they can do next
3. Never retry an operation that returns a SYSTEM STATUS or SYSTEM ERROR prefix
4. For container operations:
- First verify Docker is running and containers exist
- Provide clear next steps for users if no containers are found
Remember: A "no containers" state is a valid system state, not an error condition requiring retry."""

haiku_prompt = """You are a helpful AI assistant using Claude Haiku.
Use the available tools when needed to provide accurate and up-to-date information.
16 changes: 16 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[project]
name = "devsecops"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.9"
dependencies = [
"docker>=7.1.0",
"duckduckgo-search>=6.3.7",
"langchain-anthropic>=0.3.0",
"langchain-community>=0.3.9",
"langchain-ollama>=0.2.1",
"langgraph>=0.2.56",
"langsmith>=0.1.147",
"python-gitlab>=5.1.0",
]
26 changes: 20 additions & 6 deletions tool_docker.py
Original file line number Diff line number Diff line change
@@ -47,22 +47,36 @@ def _initialize_docker_client():
"For Colima, run: 'colima start' and try again."
)

def list_containers(self, show_all: bool = False) -> str:
"""List Docker containers."""
def list_containers(self, show_all: Any = True) -> str:
"""
List Docker containers with support for both string and boolean show_all parameter.
Args:
show_all: Can be boolean True/False or string 'True'/'False'
"""
try:
containers = self.client.containers.list(all=show_all)
# Handle both string and boolean input
if isinstance(show_all, str):
show_all_bool = show_all.lower() == 'true'
else:
show_all_bool = bool(show_all)

# Try to connect and list containers
containers = self.client.containers.list(all=show_all_bool)

if not containers:
return "No containers found"
return "SYSTEM STATUS: No Docker containers found. Docker daemon is running but no containers exist."

result = "CONTAINER ID\tIMAGE\tSTATUS\tNAMES\n"
for container in containers:
result += f"{container.short_id}\t{container.image.tags[0] if container.image.tags else 'none'}\t{container.status}\t{container.name}\n"

return result

except docker.errors.APIError as e:
return f"SYSTEM ERROR: Docker daemon not responding. Error: {str(e)}"
except Exception as e:
return f"Error listing containers: {str(e)}"
return f"SYSTEM ERROR: {str(e)}"


def _extract_log_patterns(self, logs: str) -> Dict[str, Any]:
"""Analyze logs for common patterns and anomalies."""
Loading

0 comments on commit bbcd931

Please sign in to comment.