99import os
1010import json
1111import requests
12- from typing import List , Dict , Any , Optional
12+ import logging
13+ from typing import List , Dict , Any , Optional , Union
1314from dotenv import load_dotenv
15+ from logger import get_logger
1416
1517# Load environment variables
1618load_dotenv ()
1719
18- # Default API endpoint
20+ # Set up logging
21+ logger = get_logger ("cursor_agent" )
22+
23+ # RAG API endpoint configuration
1924RAG_API_URL = os .getenv ("RAG_API_URL" , "http://localhost:8001" )
2025
26+ # Check if we should use mock embeddings
27+ USE_MOCK_EMBEDDINGS = os .environ .get ("MOCK_EMBEDDINGS" , "" ).lower () in (
28+ "true" ,
29+ "1" ,
30+ "t" ,
31+ )
32+
33+
34+ def mock_embedding (text : str ) -> List [float ]:
35+ """Mock embedding function that returns a fixed vector."""
36+ return [0.1 ] * 1536 # Same dimensionality as OpenAI's text-embedding-ada-002
37+
2138
2239def query_rag (
2340 query : str ,
@@ -47,6 +64,21 @@ def query_rag(
4764 "context" : {"current_file" : current_file } if current_file else None ,
4865 }
4966
67+ # Check for mock mode
68+ if USE_MOCK_EMBEDDINGS :
69+ return {
70+ "context_chunks" : [
71+ {
72+ "content" : "This is mock content for testing" ,
73+ "source" : "Mock source" ,
74+ "metadata" : {"type" : "mock" , "filepath" : "mock_file.json" },
75+ "similarity" : 0.95 ,
76+ }
77+ ],
78+ "suggested_prompt" : f"Query: { query } \n \n Relevant context from mock data" ,
79+ "mock_used" : True ,
80+ }
81+
5082 # Make the request
5183 response = requests .post (endpoint , json = payload )
5284 response .raise_for_status () # Raise exception for HTTP errors
@@ -55,24 +87,26 @@ def query_rag(
5587 return response .json ()
5688
5789 except requests .exceptions .RequestException as e :
58- print (f"Error querying Ignition RAG API: { e } " )
90+ logger . error (f"Error querying Ignition RAG API: { e } " )
5991 return {"context_chunks" : [], "suggested_prompt" : None , "error" : str (e )}
6092
6193
6294def get_cursor_context (
63- query : str , cursor_context : Dict [str , Any ], top_k : int = 3
95+ user_query : str ,
96+ cursor_context : Optional [Dict [str , Any ]] = None ,
97+ top_k : int = 5 ,
6498) -> str :
6599 """
66- Get context from the RAG system specifically formatted for Cursor Agent.
67- This function integrates with Cursor's context format .
100+ Get relevant context for a Cursor query based on the current cursor position
101+ and user query .
68102
69103 Args:
70- query : The user's query to search for relevant context
71- cursor_context: Context provided by Cursor about the current environment
72- top_k: Number of results to return
104+ user_query : The user's query text
105+ cursor_context: Dictionary containing cursor context (current file, selection, etc.)
106+ top_k: Number of top results to return
73107
74108 Returns:
75- String containing the formatted context for the LLM prompt
109+ A string containing the suggested prompt with context
76110 """
77111 # Extract relevant information from cursor context
78112 current_file = cursor_context .get ("current_file" )
@@ -89,7 +123,10 @@ def get_cursor_context(
89123
90124 # Query the RAG system
91125 result = query_rag (
92- query = query , top_k = top_k , filter_type = filter_type , current_file = current_file
126+ query = user_query ,
127+ top_k = top_k ,
128+ filter_type = filter_type ,
129+ current_file = current_file ,
93130 )
94131
95132 # Extract or build the context string
@@ -99,7 +136,7 @@ def get_cursor_context(
99136 # If no suggested prompt, but we have context chunks, build our own context
100137 context_chunks = result .get ("context_chunks" , [])
101138 if context_chunks :
102- context_str = f"Relevant Ignition project context for: { query } \n \n "
139+ context_str = f"Relevant Ignition project context for: { user_query } \n \n "
103140
104141 for i , chunk in enumerate (context_chunks , 1 ):
105142 source = chunk .get ("source" , "Unknown source" )
@@ -112,93 +149,160 @@ def get_cursor_context(
112149 return context_str
113150
114151 # No context available
115- return f"No relevant Ignition project context found for: { query } "
152+ return f"No relevant Ignition project context found for: { user_query } "
116153
117154
118155def get_ignition_tag_info (tag_name : str ) -> dict :
119156 """
120- Get specific information about an Ignition tag by name .
157+ Get information about a specific Ignition tag.
121158
122159 Args:
123160 tag_name: The name of the tag to look up
124161
125162 Returns:
126- Dictionary with tag information or empty dict if not found
163+ Dictionary containing tag information
127164 """
128- result = query_rag (
165+ # Check if mock mode is enabled
166+ if USE_MOCK_EMBEDDINGS :
167+ logger .info (f"Using mock data for tag: { tag_name } " )
168+ return {
169+ "name" : tag_name ,
170+ "value" : 42.0 ,
171+ "path" : f"Tags/{ tag_name } " ,
172+ "tagType" : "AtomicTag" ,
173+ "dataType" : "Float8" ,
174+ "parameters" : {
175+ "engUnit" : "%" ,
176+ "description" : f"Mock description for { tag_name } " ,
177+ "engHigh" : 100 ,
178+ "engLow" : 0 ,
179+ },
180+ "mock_used" : True ,
181+ }
182+
183+ # Get tag information from the RAG system
184+ rag_results = query_rag (
129185 query = f"Tag configuration for { tag_name } " , top_k = 1 , filter_type = "tag"
130186 )
131187
132- context_chunks = result .get ("context_chunks" , [])
188+ # Extract tag info from the context
189+ context_chunks = rag_results .get ("context_chunks" , [])
133190 if not context_chunks :
134- return {}
135-
136- # Try to parse the tag JSON
137- try :
138- chunk = context_chunks [0 ]
139- content = chunk .get ("content" , "{}" )
140- tag_data = json .loads (content )
141-
142- # If it's a list (common for tag exports), look for the specific tag
143- if isinstance (tag_data , list ):
144- for tag in tag_data :
145- if tag .get ("name" ) == tag_name :
146- return tag
147-
148- # If it's just the tag itself
149- elif isinstance (tag_data , dict ) and tag_data .get ("name" ) == tag_name :
150- return tag_data
191+ logger .warning (f"No tag information found for { tag_name } " )
192+ return {"error" : f"No tag information found for { tag_name } " }
151193
152- # Otherwise return the first chunk as best effort
153- return tag_data
194+ # Process the first matching chunk
195+ for chunk in context_chunks :
196+ content = chunk .get ("content" , "" )
197+ try :
198+ # Try to parse the tag information from the content
199+ tag_info_str = content .strip ()
200+ tag_info = json .loads (tag_info_str )
201+ if "name" in tag_info and tag_info ["name" ].lower () == tag_name .lower ():
202+ return tag_info
203+ except (json .JSONDecodeError , ValueError ) as e :
204+ logger .error (f"Error parsing tag information: { e } " )
205+ continue
154206
155- except (json .JSONDecodeError , IndexError ):
156- return {}
207+ return {"error" : f"Could not parse tag information for { tag_name } " }
157208
158209
159210def get_ignition_view_component (
160211 view_name : str , component_name : Optional [str ] = None
161212) -> dict :
162213 """
163- Get information about a Perspective view or specific component.
214+ Get information about a specific view or component in an Ignition project .
164215
165216 Args:
166- view_name: The name of the Perspective view
167- component_name: Optional specific component name to look for
217+ view_name: The name of the view to look up
218+ component_name: Optional name of the component within the view
168219
169220 Returns:
170- Dictionary with view/component information or empty dict if not found
221+ Dictionary containing view or component information
171222 """
172- # Build query based on parameters
223+ # Check if mock mode is enabled
224+ if USE_MOCK_EMBEDDINGS :
225+ logger .info (
226+ f"Using mock data for view: { view_name } , component: { component_name } "
227+ )
228+ # Create a mock response
229+ if component_name :
230+ return {
231+ "view" : view_name ,
232+ "component" : component_name ,
233+ "type" : "Label" if "label" in component_name .lower () else "Tank" ,
234+ "properties" : {
235+ "x" : 100 ,
236+ "y" : 100 ,
237+ "width" : 200 ,
238+ "height" : 150 ,
239+ "text" : (
240+ f"Mock { component_name } "
241+ if "label" in component_name .lower ()
242+ else None
243+ ),
244+ },
245+ "mock_used" : True ,
246+ }
247+ else :
248+ return {
249+ "name" : view_name ,
250+ "path" : f"views/{ view_name } .json" ,
251+ "components" : [
252+ {"name" : "Tank1" , "type" : "Tank" },
253+ {"name" : "Label1" , "type" : "Label" },
254+ ],
255+ "size" : {"width" : 800 , "height" : 600 },
256+ "mock_used" : True ,
257+ }
258+
259+ # Build the query based on what we're looking for
173260 if component_name :
174261 query = f"Component { component_name } in view { view_name } "
175262 else :
176- query = f"Perspective view { view_name } "
263+ query = f"View configuration for { view_name } "
177264
178- result = query_rag (query = query , top_k = 3 , filter_type = "perspective" )
265+ # Get view/component information from the RAG system
266+ rag_results = query_rag (query = query , top_k = 2 , filter_type = "perspective" )
179267
180- context_chunks = result .get ("context_chunks" , [])
268+ # Extract view/component info from the context
269+ context_chunks = rag_results .get ("context_chunks" , [])
181270 if not context_chunks :
182- return {}
183-
184- # Process results
185- try :
186- # If looking for a specific component
187- if component_name :
188- for chunk in context_chunks :
189- content = chunk .get ("content" , "{}" )
190- component_data = json .loads (content )
191-
192- if component_data .get ("name" ) == component_name :
193- return component_data
194-
195- # Just return the first chunk's content as best effort
196- chunk = context_chunks [0 ]
197- content = chunk .get ("content" , "{}" )
198- return json .loads (content )
199-
200- except (json .JSONDecodeError , IndexError ):
201- return {}
271+ logger .warning (f"No view information found for { view_name } " )
272+ return {"error" : f"No view information found for { view_name } " }
273+
274+ # Combine the relevant context
275+ view_info = {}
276+ for chunk in context_chunks :
277+ content = chunk .get ("content" , "" )
278+ try :
279+ # Try to parse the JSON content
280+ content_obj = json .loads (content .strip ())
281+
282+ # For component search
283+ if (
284+ component_name
285+ and "name" in content_obj
286+ and content_obj ["name" ] == component_name
287+ ):
288+ return content_obj
289+
290+ # For view search
291+ if "name" in content_obj and content_obj ["name" ] == view_name :
292+ view_info = content_obj
293+ break
294+
295+ # For partial view information
296+ view_info .update (content_obj )
297+
298+ except (json .JSONDecodeError , ValueError ) as e :
299+ logger .error (f"Error parsing view information: { e } " )
300+ continue
301+
302+ if not view_info :
303+ return {"error" : f"Could not parse view information for { view_name } " }
304+
305+ return view_info
202306
203307
204308# Example of how to use in Cursor Agent mode
0 commit comments