File size: 7,018 Bytes
890d952
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
from agents import Agent, function_tool
from retriever_tool import db
from textwrap import dedent
from agents import Tool
from model import gemini_model, qwen_model

@function_tool
def unused_ports() -> str:
    """Get information about unused Ethernet interfaces across all network devices.
    
    This tool specifically queries for unused/available ports in the network infrastructure
    by filtering documents with 'Ethernet Interfaces Summary' headers and UNUSED interfaces.
    Only returns results from leaf switches (devices with 'LEAF' in their name).
    """
    
    # Use metadata-based filtering instead of similarity search
    # Get all documents from the vectorstore
    all_docs_with_scores = db.similarity_search_with_score("", k=db.index.ntotal)
    
    # Filter documents by header metadata and device type
    target_header = "Ethernet Interfaces Summary"
    matching_docs = []
    
    for doc, score in all_docs_with_scores:
        # Check if document has the target header
        section_title = doc.metadata.get('section_title', '')
        header_path = doc.metadata.get('header_path', '')
        device_name = doc.metadata.get('device_name', '')
        
        # Filter for:
        # 1. Documents with "Ethernet Interfaces Summary" in header path
        # 2. Documents from LEAF devices only
        # 3. Documents that contain UNUSED interfaces
        is_ethernet_summary = (target_header == section_title or target_header in header_path)
        is_leaf_device = 'LEAF' in device_name.upper()
        has_unused = 'UNUSED' in doc.page_content
        
        if is_ethernet_summary and is_leaf_device and has_unused:
            matching_docs.append((doc, score))
    
    response = ""
    if not matching_docs:
        return "No unused interface information found in Ethernet Interfaces Summary sections of leaf devices."

    # Track devices and their unused interfaces
    device_unused = {}
    
    for doc, score in matching_docs:
        device_name = doc.metadata.get('device_name')
        source = doc.metadata.get('source', 'Unknown source')
        header_path = doc.metadata.get('header_path', 'No header path')
        section_title = doc.metadata.get('section_title', 'No section title')
        
        if device_name:
            # Count UNUSED interfaces in this chunk
            unused_count = doc.page_content.count('UNUSED')
            
            if unused_count > 0:
                if device_name not in device_unused:
                    device_unused[device_name] = {
                        'total_unused': 0,
                        'sections': [],
                        'source': source
                    }
                
                device_unused[device_name]['total_unused'] += unused_count
                device_unused[device_name]['sections'].append({
                    'section': section_title,
                    'header_path': header_path,
                    'count': unused_count,
                    'score': score
                })
    
    # Format the response with enhanced metadata
    if matching_docs:
        # Attach the actual sections containing UNUSED interfaces from each document
        for doc, score in matching_docs:
            device_name = doc.metadata.get('device_name', 'Unknown device')
            source = doc.metadata.get('source', 'Unknown source')
            section_title = doc.metadata.get('section_title', 'No section title')
            header_path = doc.metadata.get('header_path', 'No header path')
            
            response += f"---\n"
            response += f"Device: {device_name} (Source: {source})\n"
            response += f"Section: {section_title}\n"
            response += f"Path: {header_path}\n\n"
            
            # Extract and attach only the lines that actually contain UNUSED interfaces
            for line in doc.page_content.splitlines():
                if 'UNUSED' in line:
                    response += line + "\n"
            
            response += "\n"
    else:
        response += "No leaf devices with unused interfaces found in Ethernet Interfaces Summary sections."

    print(f"Retrieved {len(matching_docs)} filtered results from Ethernet Interfaces Summary sections on leaf devices")
    return response

# Port recommendation specific instructions
port_recommendation_instructions = dedent("""
    You are an expert network assistant specialized in port and interface recommendations.
    Your role is to use the available tools to answer questions about port/interface recommendations for connecting new devices to the network infrastructure.
    
    Key responsibilities:
    - Port and interface are synonymous terms (users may use them interchangeably)
    - Always use the retrieve_network_information tool to find unused interface ports before making recommendations
    - Provide specific device names and port numbers in your recommendations
    - Be detailed and precise in your responses
    
    Port Recommendation Rules:
    1. If not specified otherwise, always recommend TWO ports across different devices for redundancy
    2. Recommend ports across devices that form a MLAG or LACP group
    3. Leaf switches are in MLAG pairs: odd-numbered leaf (leaf01, leaf03, etc.) paired with even-numbered leaf (leaf02, leaf04, etc.)
    4. Try to select the same interface port number across paired devices (e.g., if recommending port 25 on leaf01, also recommend port 25 on leaf02)
    5. Include device names and specific port identifiers in your response
    6. If user specifically requests single port or "without redundancy", recommend only one port
    7. Only recommend ports that have the description "UNUSED".
                
    
    Response Format:
    - Always query for unused ports first using retrieve_network_information
    - Provide clear, actionable recommendations with device names and port numbers
    - Explain the reasoning behind your recommendations when relevant
    
    Examples:
    User: "I need an unused port"
    Response: After checking available ports, I recommend using Ethernet1/25 on leaf01 and Ethernet1/25 on leaf02 for redundancy.
    
    User: "I need an unused port without redundancy"  
    Response: After checking available ports, I recommend using Ethernet1/26 on leaf01.
    
    User: "I need to dual connect a server to the network, what ports should I use?"
    Response: For dual-connecting a server, I recommend using Ethernet1/27 on leaf01 and Ethernet1/27 on leaf02, which will provide MLAG redundancy.
    
    User: "I need to connect two servers to the network, what ports should I use?"
    Response: For connecting two servers, I recommend using Ethernet1/28-29 on leaf01 and Ethernet1/28-29 on leaf02 for redundancy.
""")


# Create the specialized port recommendations agent
port_recommendations_agent = Agent(
    name="port_recommendations_agent",
    instructions=port_recommendation_instructions,
    model=qwen_model,
    tools=[unused_ports],
)