-
Notifications
You must be signed in to change notification settings - Fork 0
Ben/category enrichment #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
782275c
543c28d
aab339f
138e84d
8f8dd75
84117ef
06f8203
2a304ac
51a158e
6afa9f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,15 +18,15 @@ class AgentSettings(BaseSettings): | |
| EMBEDDER_MODEL: str = "nomic-embed-text:latest" | ||
| EMBEDDER_KEY: str = "" | ||
| HYBRID_SEARCH_MAX_TABLES: int = 10 | ||
| MAX_PROFILES_TO_FETCH: int = 3 | ||
| MAX_PROFILES_TO_FETCH: int = 8 | ||
| PROFILE_FETCH_CONCURRENCY: int = Field(default=4, gt=0) | ||
| LANGFUSE_SECRET_KEY: str = Field(min_length=1) | ||
| LANGFUSE_PUBLIC_KEY: str = Field(min_length=1) | ||
| LANGFUSE_BASE_URL: str = Field(min_length=1) | ||
|
|
||
| # ── Jeen Integration ────────────────────────────────────────────────────── | ||
| JEEN_LLM_CORE_URL: str = "" # If empty, agent gracefully skips fetching | ||
| JEEN_API_KEY: str = "" # If empty, agent gracefully skips fetching | ||
| JEEN_LLM_CORE_URL: str = "http://schema-modeler.dev161.internal/api/mcp" # If empty, agent gracefully skips fetching | ||
| JEEN_API_KEY: str = "mcp_ecd023ab04f849b36aef5d797525365c4c095052e6e07577d065bfe82507ae67" # If empty, agent gracefully skips fetching | ||
| SKILLS_HOT_RELOAD: bool = False # If true, bypass Redis cache for skills | ||
| NOMINATIM_USER_AGENT: str = "text2sql-agent/1.0" # Nominatim acceptable-use identifier | ||
| NOMINATIM_URL: str = "https://nominatim.openstreetmap.org/search" | ||
|
|
@@ -57,16 +57,19 @@ class AgentSettings(BaseSettings): | |
| LANGFUSE_PROMPT_SCHEMA_EXPLORER: str = "text2sql/schema_explorer" | ||
| LANGFUSE_PROMPT_QUERY_BUILDER: str = "text2sql/query_builder" | ||
| LANGFUSE_PROMPT_REFINER: str = "text2sql/refiner" | ||
| LANGFUSE_PROMPT_FINALIZER_SUMMARY: str = "text2sql/finalizer_summary" | ||
| LANGFUSE_PROMPT_FINALIZER_SQL_EXPLANATION: str = ( | ||
| "text2sql/finalizer_sql_explanation" | ||
| ) | ||
| LANGFUSE_PROMPT_FINALIZER: str = "text2sql/finalizer" | ||
| LANGFUSE_PROMPT_REJECTION_ROUTER: str = "text2sql/rejection_router" | ||
| LANGFUSE_PROMPT_CATEGORY_ENRICHMENT: str = "text2sql/category_enrichment" | ||
| LANGFUSE_PROMPT_LOC_EXTRACTOR: str = "text2sql/extractor" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find all uses of the two extractor prompt settings and their prompt templates.
rg -n -C3 'LANGFUSE_PROMPT_LOC_EXTRACTOR\b|LANGFUSE_PROMPT_EXTRACTOR\b'
fd -i 'location' --extension txt --extension md --exec cat -n {}Repository: StavPonte11/text2sql-onboarding Length of output: 169 🏁 Script executed: #!/bin/bash
set -u
echo "== repo files sample =="
git ls-files | sed -n '1,120p'
echo "== config file =="
fd -i 'config.py' -x sh -c 'echo "--- $0"; wc -l "$0"; sed -n "1,120p" "$0"' {}
echo "== prompt extractor occurrences case-insensitive full text =="
rg -n -C3 'LANGFUSE_PROMPT(extractor|LOC_EXTRACTOR)|text2sql/extractor|text2sql/location|extractor' . || true
echo "== locate location extractor files =="
fd -i 'location' --extension py --extension ts --extension js --extension txt --extension md || trueRepository: StavPonte11/text2sql-onboarding Length of output: 50389 🏁 Script executed: #!/bin/bash
set -u
echo "== agent config relevant lines =="
sed -n '45,75p' agent/src/agent/config.py
echo "== location extractor implementation =="
fd -i 'location_extractor.py' -x sh -c 'echo "--- $0"; wc -l "$0"; sed -n "$1,\$p" "$0"' {} \;
echo "== upload script relevant prompt/setting entries =="
sed -n '16,45p;70,115p;180,235p' agent/scripts/upload_all_prompts.py
echo "== config references to location prompt identifiers =="
rg -n -C2 'LANGFUSE_PROMPT_LOC_EXTRACTOR|LANGFUSE_PROMPT_LOC_EXTRACTOR_INSTRUCTION|text2sql/extractor|text2sql/location' agent/src/agent agent/scripts/worker || true
echo "== all config.py references exact identifiers =="
rg -n 'LANGFUSE_PROMPT_(LOC_EXTRACTOR|EXTRACTOR|LOC_EXTRACTOR_INSTRUCTION)' . || trueRepository: StavPonte11/text2sql-onboarding Length of output: 18556 🏁 Script executed: #!/bin/bash
set -u
echo "== location_extractor_implementation sections =="
sed -n '1,260p' agent/src/agent/services/location_extractor.py | cat -n
echo "== tests relevant expectations/calls =="
sed -n '135,230p' agent/tests/test_location_extractor.py | cat -n
echo "== upload script location/instruction references =="
rg -n -C4 'location|extractor|LOCATION|instruction|wkt' agent/scripts/upload_all_prompts.py || trueRepository: StavPonte11/text2sql-onboarding Length of output: 20683 Point
🤖 Prompt for AI Agents |
||
| LANGFUSE_PROMPT_LOC_EXTRACTOR_INSTRUCTION: str = ( | ||
| "text2sql/location_wkt_instruction" | ||
| ) | ||
| LANGFUSE_PROMPT_REFINER_STEP1: str = "text2sql/refiner_step1" | ||
| LANGFUSE_PROMPT_REFINER_STEP2: str = "text2sql/refiner_step2" | ||
| LANGFUSE_PROMPT_DETECT_AMBIGUITY: str = "text2sql/detect_ambiguity" | ||
|
|
||
| MAX_REFINER_ITERATIONS: int = Field(default=3, gt=0) | ||
| REFINER_SCHEMA_CONTEXT_TABLES: int = Field(default=4, gt=0) | ||
| MAX_REFINER_ITERATIONS: int = Field(default=10, gt=0) | ||
| REFINER_SCHEMA_CONTEXT_TABLES: int = Field(default=8, gt=0) | ||
|
Comment on lines
+71
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win Assess the cost of 10 refiner iterations.
🤖 Prompt for AI Agents |
||
|
|
||
| # ── G2-01: Table Scoping ────────────────────────────────────────────────── | ||
| DEFAULT_TABLE_SCOPING_MODE: Literal["strict", "hybrid"] = "hybrid" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,39 @@ | ||
| import warnings | ||
| import urllib3 | ||
| from langfuse import Langfuse | ||
| from opentelemetry import trace as otel_trace_api | ||
| from agent.config import settings | ||
|
|
||
| # Suppress unverified HTTPS warnings for dev internal endpoints | ||
| urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) | ||
| warnings.filterwarnings("ignore", category=urllib3.exceptions.InsecureRequestWarning) | ||
|
Comment on lines
+7
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Global warning suppression hides TLS problems for the whole process.
Restrict the suppression to the development case, for example by gating it on a setting, and keep verification enabled elsewhere. 🔒 Proposed fix-# Suppress unverified HTTPS warnings for dev internal endpoints
-urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
-warnings.filterwarnings("ignore", category=urllib3.exceptions.InsecureRequestWarning)
+# Suppress unverified HTTPS warnings for dev internal endpoints only.
+if getattr(settings, "ALLOW_INSECURE_INTERNAL_TLS", False):
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+ warnings.filterwarnings(
+ "ignore", category=urllib3.exceptions.InsecureRequestWarning
+ )🤖 Prompt for AI Agents |
||
|
|
||
| langfuse_client = Langfuse( | ||
| public_key=settings.LANGFUSE_PUBLIC_KEY, | ||
| secret_key=settings.LANGFUSE_SECRET_KEY, | ||
| host=settings.LANGFUSE_BASE_URL, | ||
| ) | ||
|
|
||
| # Patch update_current_span and get_current_trace_id to safely no-op when running without an active OpenTelemetry span context | ||
| _orig_update_current_span = langfuse_client.update_current_span | ||
| _orig_get_current_trace_id = langfuse_client.get_current_trace_id | ||
|
|
||
|
|
||
| def _safe_update_current_span(*args, **kwargs): | ||
| current_span = otel_trace_api.get_current_span() | ||
| if current_span is otel_trace_api.INVALID_SPAN: | ||
| return | ||
| return _orig_update_current_span(*args, **kwargs) | ||
|
|
||
|
|
||
| def _safe_get_current_trace_id(*args, **kwargs): | ||
| current_span = otel_trace_api.get_current_span() | ||
| if current_span is otel_trace_api.INVALID_SPAN: | ||
| return None | ||
| return _orig_get_current_trace_id(*args, **kwargs) | ||
|
Comment on lines
+22
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: In OpenTelemetry Python, get_current_span is used to retrieve the span currently stored in the execution context [1][2]. If no span is present in the context, it returns an INVALID_SPAN object [3][1]. INVALID_SPAN is a specific instance of a NonRecordingSpan [3][4]. A NonRecordingSpan is a "no-op" span implementation used when no full tracing SDK or instrumentation is active [3][5][6]. Its primary purpose is to allow applications to continue using the OpenTelemetry API (e.g., propagating trace contexts) without crashing or requiring a fully functional tracing backend [3][5]. Key points regarding these components: 1. INVALID_SPAN: This is the default returned by get_current_span when no active span is found [1][2]. It contains an "invalid" SpanContext [4]. 2. NonRecordingSpan: This class implements the Span interface but performs no actual recording of events, attributes, or status updates [3][6]. Its method is_recording consistently returns False [5][6]. 3. SpanContext and is_valid: The SpanContext associated with a span has a property called is_valid [3][6]. This property indicates whether the context contains valid trace and span identifiers [3][7]. In the case of INVALID_SPAN, the associated SpanContext has is_valid set to False (or its equivalent state indicating an invalid/empty context) [5]. You can check the validity of a span's context by accessing span.get_span_context.is_valid [3][6]. If you find that get_current_span is returning an INVALID_SPAN unexpectedly, it often means that no span has been activated in the current execution context [8]. This typically occurs if you have not started a span using a tracer (e.g., using tracer.start_as_current_span or the context manager) or if the scope where the span was activated has already closed [8]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Repository files matching langfuse_client.py:"
fd -a 'langfuse_client.py' . || true
echo
echo "Relevant file excerpt:"
if [ -f agent/src/agent/langfuse_client.py ]; then
cat -n agent/src/agent/langfuse_client.py | sed -n '1,120p'
else
echo "file not found"
fi
echo
echo "OpenTelemetry API/imports and span context usage:"
rg -n "INVALID_SPAN|get_current_span|NonRecordingSpan|is_valid|get_span_context" -S . --glob '!node_modules' --glob '!dist' --glob '!build' || true
echo
echo "Check installed opentelemetry API availability and behavior without importing repo code:"
python3 - <<'PY'
import importlib.util
for mod in ['opentelemetry.trace','opentelemetry.trace.span']:
print(f'{mod}: {importlib.util.find_spec(mod) is not None}')
if importlib.util.find_spec('opentelemetry.trace') is not None:
import opentelemetry.trace as trace_api
print('INVALID_SPAN id:', id(trace_api.INVALID_SPAN))
print('INVALID_SPAN __class__:', trace_api.INVALID_SPAN.__class__)
print('INVALID_SPAN is_valid:', trace_api.INVALID_SPAN.get_span_context().is_valid if hasattr(trace_api.INVALID_SPAN,'get_span_context') else 'no get_span_context')
print('get_current_span():', trace_api.get_current_span())
print('current is same INSTANCE:', trace_api.get_current_span() is trace_api.INVALID_SPAN)
print('current is_valid:', trace_api.get_current_span().get_span_context().is_valid if hasattr(trace_api.get_current_span(),'get_span_context') else 'no get_span_context')
PYRepository: StavPonte11/text2sql-onboarding Length of output: 2762 🌐 Web query:
💡 Result: In OpenTelemetry Python, the behavior of span-related objects and the retrieval of the current span are designed to ensure applications do not crash when tracing is not active or when a span is explicitly configured not to record data [1][2]. get_current_span and InvalidSpan The function opentelemetry.trace.get_current_span is used to retrieve the currently active span from the context [3][4]. If no span is active in the current context, it returns an INVALID_SPAN object [3][4]. INVALID_SPAN is a singleton that implements the Span interface as a no-op; calling methods on it (such as set_attribute or end) does nothing, ensuring that your application code remains safe even if the tracing infrastructure is not initialized [5][2]. NonRecordingSpan A NonRecordingSpan is a specific type of span that exists to facilitate context propagation even when a span is not intended to be recorded (e.g., due to sampling decisions) [5][1]. - Functionality: Like INVALID_SPAN, a NonRecordingSpan is essentially a no-op span [5]. Its primary purpose is to hold SpanContext information (such as TraceId and SpanId) so that trace context can be propagated to downstream services without incurring the overhead of recording span data [5][6]. - Propagation: When trace context is extracted (e.g., from incoming HTTP headers), a NonRecordingSpan may be created if the incoming context indicates that the trace should not be sampled (e.g., the sampled flag is set to 0 in the W3C traceparent header) [7][6][8]. - Confusion in Behavior: Users often encounter confusion when they see spans are not recorded when a traceparent header is present [7][8]. This is typically due to the default ParentBased sampler, which respects the sampling decision propagated from the upstream service [7]. If the upstream service decided not to sample the request, the Python application will create a NonRecordingSpan for the child, causing it to appear as if tracing is broken [7][8]. Summary of Key Differences - INVALID_SPAN: Returned by get_current_span when absolutely no span is associated with the current context [3][4]. It is the "default" fallback [2]. - NonRecordingSpan: Returned when a context exists and needs to be propagated, but the sampling configuration (or inherited sampling decision) dictates that no span data should be recorded [5][7][6]. If you are experiencing unexpected non-recording behavior, verify your sampling configuration (e.g., OTEL_TRACES_SAMPLER environment variable) and ensure you are not explicitly passing or inheriting a non-sampled context [7][8]. Citations:
🌐 Web query:
💡 Result: In the OpenTelemetry Python API, NonRecordingSpan is a concrete implementation of the Span interface used when no functional tracing SDK is configured or active [1][2]. Its primary purpose is to allow applications to use the OpenTelemetry API without side effects, while still supporting context propagation [1][3]. NonRecordingSpan and its methods behave as follows: is_recording: This method always returns False for a NonRecordingSpan [2][4]. This signals that the span is not capturing any data such as attributes, events, or status updates [3]. All operations that would typically record data (e.g., set_attribute, add_event) are no-op in this class [2][4]. get_span_context: This method returns the SpanContext associated with the NonRecordingSpan [2][4]. Even when a span is non-recording, it may hold a valid SpanContext to ensure that trace and span identifiers can be propagated to downstream services or child spans [3][5]. SpanContext.is_valid: The is_valid property on a SpanContext indicates whether the context contains a properly formatted, non-zero trace ID and span ID [6][7]. A SpanContext is considered valid if the trace ID is greater than the INVALID_TRACE_ID and the span ID is greater than the INVALID_SPAN_ID [7]. In summary, a NonRecordingSpan is a lightweight, no-op span that maintains a SpanContext to facilitate trace propagation, but does not perform any data recording [3][4]. Its is_recording method consistently returns False [2][4], while the validity of its context is determined by the internal state of the attached SpanContext [7]. Citations:
Test span recording and context validity, not object identity.
🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| langfuse_client.update_current_span = _safe_update_current_span | ||
| langfuse_client.get_current_trace_id = _safe_get_current_trace_id | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,3 +69,4 @@ def get_llm( | |
| temperature=temperature, | ||
| timeout=300.0, | ||
| ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,4 @@ | ||||||||||||
| import json | ||||||||||||
| import asyncio | ||||||||||||
| from langchain_core.runnables.config import RunnableConfig | ||||||||||||
| from agent.utils.redis_publisher import publish_node_event | ||||||||||||
| from agent.state import AgentState | ||||||||||||
|
|
@@ -9,25 +8,21 @@ | |||||||||||
| from agent.llm import get_llm | ||||||||||||
| from agent.utils.esca import get_esca_client | ||||||||||||
|
|
||||||||||||
| from agent.utils.esca import get_esca_client | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| async def get_esca_preview(esca_id: str, limit: int = 5) -> str: | ||||||||||||
| async def get_esca_preview(esca_id: str, limit: int = 10) -> str: | ||||||||||||
| """Load data from Esca and return a preview of the columns and the first few rows.""" | ||||||||||||
| if not esca_id: | ||||||||||||
| return "No data reference found." | ||||||||||||
|
|
||||||||||||
| try: | ||||||||||||
| async with get_esca_client() as client: | ||||||||||||
| # TODO: instead of fetching everything from esca and then chunk, get only the chunk | ||||||||||||
| data_bytes = await client.load_head(esca_id) | ||||||||||||
| data = json.loads(data_bytes.decode()) | ||||||||||||
|
|
||||||||||||
| columns = data.get("columns", []) | ||||||||||||
| rows = data.get("rows", []) | ||||||||||||
| total_rows = len(rows) | ||||||||||||
|
|
||||||||||||
| # Take a slice of the rows to avoid context overload | ||||||||||||
| preview_rows = rows[:limit] | ||||||||||||
|
|
||||||||||||
| preview_info = { | ||||||||||||
|
|
@@ -36,51 +31,41 @@ async def get_esca_preview(esca_id: str, limit: int = 5) -> str: | |||||||||||
| "preview_count": len(preview_rows), | ||||||||||||
| "total_rows": total_rows, | ||||||||||||
| } | ||||||||||||
| return json.dumps(preview_info, indent=2) | ||||||||||||
| return json.dumps(preview_info, indent=2, default=str) | ||||||||||||
| except Exception as e: | ||||||||||||
| return f"Error retrieving data preview from Esca: {e}" | ||||||||||||
|
Comment on lines
35
to
36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win Log the exception and return a neutral preview message.
🛡️ Proposed fix- except Exception as e:
- return f"Error retrieving data preview from Esca: {e}"
+ except Exception:
+ logger.exception("Failed to retrieve data preview from Esca for id=%s", esca_id)
+ return "Data preview is unavailable."Add the logger import if it is absent: import logging
logger = logging.getLogger(__name__)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| async def get_sql_explanation(sql_query: str | None, llm) -> str: | ||||||||||||
| """Ask LLM to explain the SQL query in natural language.""" | ||||||||||||
| if not sql_query: | ||||||||||||
| return "No SQL query was generated." | ||||||||||||
|
|
||||||||||||
| langfuse_prompt = langfuse_client.get_prompt( | ||||||||||||
| settings.LANGFUSE_PROMPT_FINALIZER_SQL_EXPLANATION | ||||||||||||
| ) | ||||||||||||
| prompt_sql_explanation = ChatPromptTemplate.from_messages( | ||||||||||||
| langfuse_prompt.get_langchain_prompt() | ||||||||||||
| ) | ||||||||||||
|
|
||||||||||||
| chain = prompt_sql_explanation | llm | ||||||||||||
| response = await chain.ainvoke({"sql_query": sql_query}) | ||||||||||||
| return response.content | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| async def finalizer_node(state: AgentState, config: RunnableConfig | None = None): | ||||||||||||
| """Summarize data.""" | ||||||||||||
| """Summarize data using the unified Hebrew finalizer prompt.""" | ||||||||||||
| thread_id = config.get("configurable", {}).get("thread_id", "") if config else "" | ||||||||||||
| from agent.utils.redis_publisher import publish_node_event | ||||||||||||
| await publish_node_event(thread_id, "finalizer") | ||||||||||||
|
|
||||||||||||
| raw_data_ref = state.get("raw_data_ref") | ||||||||||||
| esca_write_failed = state.get("esca_write_failed", False) | ||||||||||||
| inline_result_rows = state.get("inline_result_rows") | ||||||||||||
| inline_result_columns = state.get("inline_result_columns") | ||||||||||||
| runtime_flags = state.get("runtime_flags") or {} | ||||||||||||
| llm = get_llm("finalizer", runtime_flags=runtime_flags) | ||||||||||||
|
|
||||||||||||
| esca_write_enabled = str(runtime_flags.get("ESCA_WRITE_ENABLED", settings.ESCA_WRITE_ENABLED)).lower() == "true" | ||||||||||||
|
|
||||||||||||
| esca_write_enabled = ( | ||||||||||||
| str( | ||||||||||||
| runtime_flags.get("ESCA_WRITE_ENABLED", settings.ESCA_WRITE_ENABLED) | ||||||||||||
| ).lower() | ||||||||||||
| == "true" | ||||||||||||
| ) | ||||||||||||
|
|
||||||||||||
| preview_str = "" | ||||||||||||
| if not esca_write_enabled: | ||||||||||||
| if not esca_write_enabled or not raw_data_ref: | ||||||||||||
| if inline_result_rows is not None: | ||||||||||||
| limit = 5 | ||||||||||||
| limit = 10 | ||||||||||||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know this is not your code but we need to add an env variable |
||||||||||||
| preview_rows = inline_result_rows[:limit] | ||||||||||||
| columns = ( | ||||||||||||
| list(preview_rows[0].keys()) | ||||||||||||
| if preview_rows and isinstance(preview_rows[0], dict) | ||||||||||||
| else [] | ||||||||||||
| ) | ||||||||||||
| if inline_result_columns: | ||||||||||||
| columns = inline_result_columns | ||||||||||||
| elif preview_rows and isinstance(preview_rows[0], dict): | ||||||||||||
| columns = list(preview_rows[0].keys()) | ||||||||||||
| else: | ||||||||||||
| columns = [] | ||||||||||||
|
|
||||||||||||
| preview_info = { | ||||||||||||
| "columns": columns, | ||||||||||||
| "preview_rows": preview_rows, | ||||||||||||
|
|
@@ -91,32 +76,28 @@ async def finalizer_node(state: AgentState, config: RunnableConfig | None = None | |||||||||||
| else: | ||||||||||||
| preview_str = "No data reference found." | ||||||||||||
| else: | ||||||||||||
| preview_str = await get_esca_preview(raw_data_ref) | ||||||||||||
| preview_str = await get_esca_preview(raw_data_ref, limit=10) | ||||||||||||
|
|
||||||||||||
| langfuse_prompt_summary = langfuse_client.get_prompt( | ||||||||||||
| settings.LANGFUSE_PROMPT_FINALIZER_SUMMARY | ||||||||||||
| prompt_name = getattr( | ||||||||||||
| settings, "LANGFUSE_PROMPT_FINALIZER", "text2sql/finalizer" | ||||||||||||
| ) | ||||||||||||
| prompt_summary = ChatPromptTemplate.from_messages( | ||||||||||||
| langfuse_prompt_summary.get_langchain_prompt() | ||||||||||||
| langfuse_prompt = langfuse_client.get_prompt(prompt_name) | ||||||||||||
|
Comment on lines
+81
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check the declared Langfuse prompt settings fields.
fd -t f 'config.py' agent/src | xargs rg -n 'LANGFUSE_PROMPT'Repository: StavPonte11/text2sql-onboarding Length of output: 982 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== settings class files =="
fd -t f 'config.py' agent/src
echo
echo "== finalizer node context =="
fd -t f 'finalizer.py' agent/src/agent/nodes | xargs -r sed -n '1,130p'
echo
echo "== query_builder context =="
fd -t f 'query_builder.py' agent/src/agent/nodes | xargs -r sed -n '1,130p'Repository: StavPonte11/text2sql-onboarding Length of output: 8643 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== all Langfuse prompt usages in nodes =="
fd -t f -i '*.py' agent/src/agent/nodes | xargs rg -n 'LANGFUSE_PROMPT|get_prompt|getattr' || true
echo
echo "== direct attribute access pattern for prompt names =="
fd -t f -i '*.py' agent/src/agent/nodes | xargs rg -n 'settings\.LANGFUSE_PROMPT|getattr\([^,]+,\s*"LANGFUSE' || trueRepository: StavPonte11/text2sql-onboarding Length of output: 11398 Replace the fallback with direct attribute access for
🤖 Prompt for AI Agents |
||||||||||||
| prompt_finalizer = ChatPromptTemplate.from_messages( | ||||||||||||
| langfuse_prompt.get_langchain_prompt() | ||||||||||||
| ) | ||||||||||||
|
|
||||||||||||
| summary_chain = prompt_summary | llm | ||||||||||||
|
|
||||||||||||
| summary_task = summary_chain.ainvoke( | ||||||||||||
| chain = prompt_finalizer | llm | ||||||||||||
| response = await chain.ainvoke( | ||||||||||||
| { | ||||||||||||
| "user_query": state["user_query"], | ||||||||||||
| "user_request": state.get("user_query") or "", | ||||||||||||
| "sql_query": state.get("sql_query") or "", | ||||||||||||
| "raw_data_ref": raw_data_ref, | ||||||||||||
| "data_preview": preview_str, | ||||||||||||
| "sql_translation": state.get("sql_explanation") or "", | ||||||||||||
| "sql_results": preview_str, | ||||||||||||
| } | ||||||||||||
| ) | ||||||||||||
|
|
||||||||||||
| sql_task = get_sql_explanation(state.get("sql_query"), llm) | ||||||||||||
|
|
||||||||||||
| summary_response, sql_explanation = await asyncio.gather(summary_task, sql_task) | ||||||||||||
|
|
||||||||||||
| return { | ||||||||||||
| "summary": summary_response.content, | ||||||||||||
| "sql_explanation": sql_explanation, | ||||||||||||
| "summary": response.content, | ||||||||||||
| "sql_explanation": state.get("sql_explanation", ""), | ||||||||||||
| "execution_path": ["finalizer"], | ||||||||||||
| } | ||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
Remove the hard-coded API key from source.
Line 29 commits a live-looking
mcp_...bearer token as the default value ofJEEN_API_KEY. The value is now in Git history and is readable by anyone with repository access. The same pattern exists at line 42 forJEEN_METADATA_MCP_KEY. Restore empty defaults and supply the keys through the environment. Rotate both keys, because they must be treated as compromised.The inline comment "If empty, agent gracefully skips fetching" also no longer matches a non-empty default.
🔒 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents