Spaces:
Running
Running
Merge branch 'tweaks'
Browse files- api_client.py +0 -1
- api_monitor.py +72 -50
- main.py → app.py +3 -3
- requirements.txt +1 -0
api_client.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1 |
import requests
|
2 |
import json
|
3 |
-
import psycopg2
|
4 |
|
5 |
|
6 |
def parse_key_value_string(key_value_string):
|
|
|
1 |
import requests
|
2 |
import json
|
|
|
3 |
|
4 |
|
5 |
def parse_key_value_string(key_value_string):
|
api_monitor.py
CHANGED
@@ -10,6 +10,7 @@ from dotenv import load_dotenv
|
|
10 |
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
11 |
import requests
|
12 |
|
|
|
13 |
|
14 |
# Load environment variables from .env file
|
15 |
load_dotenv(override=True)
|
@@ -25,11 +26,18 @@ def connect_to_db():
|
|
25 |
raise ValueError(
|
26 |
"Database password not found in environment variables. Please set DB_PASSWORD."
|
27 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
return psycopg2.connect(
|
29 |
-
host=
|
30 |
-
port=
|
31 |
-
database=
|
32 |
-
user=
|
33 |
password=db_password,
|
34 |
cursor_factory=psycopg2.extras.DictCursor,
|
35 |
)
|
@@ -89,7 +97,7 @@ def validate_api_configuration(
|
|
89 |
additional_params,
|
90 |
schedule_interval_minutes,
|
91 |
stop_after_hours,
|
92 |
-
start_at,
|
93 |
):
|
94 |
"""
|
95 |
TOOL: Validate and store API configuration for monitoring.
|
@@ -101,6 +109,10 @@ def validate_api_configuration(
|
|
101 |
before proceeding to activate_monitoring(). The API call may return success=True but contain
|
102 |
error messages (like "401 Unauthorized", "Invalid API key", etc.) in the sample_response.
|
103 |
|
|
|
|
|
|
|
|
|
104 |
WORKFLOW:
|
105 |
1. Call this function to validate API configuration
|
106 |
2. If success=False: Fix parameters and retry this function
|
@@ -108,7 +120,7 @@ def validate_api_configuration(
|
|
108 |
4. If sample_response contains error messages: Fix API parameters and retry validation
|
109 |
5. If sample_response looks valid: Use config_id in activate_monitoring() to activate monitoring
|
110 |
|
111 |
-
|
112 |
- mcp_api_key: MCP API key serves as user identifier
|
113 |
- name: User-friendly name for the monitoring task
|
114 |
- description: Description of what is being monitored
|
@@ -117,11 +129,16 @@ def validate_api_configuration(
|
|
117 |
- endpoint: The specific API endpoint
|
118 |
- param_keys_values: Parameter key-value pairs, one per line
|
119 |
- header_keys_values: Header key-value pairs, one per line
|
120 |
-
- additional_params: Optional JSON string for complex parameters
|
|
|
121 |
- stop_after_hours: Hours after which to stop (supports decimals, max 168 = 1 week)
|
122 |
-
- start_at:
|
|
|
|
|
123 |
|
124 |
-
Input Examples:
|
|
|
|
|
125 |
mcp_api_key: "your_mcp_key_here"
|
126 |
name: "NVDA Stock Price"
|
127 |
description: "Monitor NVIDIA stock price every 30 minutes"
|
@@ -135,18 +152,18 @@ def validate_api_configuration(
|
|
135 |
stop_after_hours: 1.5
|
136 |
start_at: ""
|
137 |
|
138 |
-
2.
|
139 |
mcp_api_key: "your_mcp_key_here"
|
140 |
-
name: "Weather
|
141 |
-
description: "Monitor
|
142 |
-
method: "
|
143 |
-
base_url: "https://api.
|
144 |
-
endpoint: "
|
145 |
-
param_keys_values: "
|
146 |
-
header_keys_values: "
|
147 |
-
additional_params:
|
148 |
-
schedule_interval_minutes:
|
149 |
-
stop_after_hours:
|
150 |
start_at: "2024-06-15 09:00:00"
|
151 |
|
152 |
Returns:
|
@@ -160,7 +177,8 @@ def validate_api_configuration(
|
|
160 |
"sample_response": {...},
|
161 |
"stop_at": "2025-06-11T12:00:00Z",
|
162 |
"start_at": "2025-06-04T12:00:00Z"
|
163 |
-
}
|
|
|
164 |
"""
|
165 |
try:
|
166 |
# Validate input parameters
|
@@ -357,7 +375,7 @@ async def activate_monitoring(config_id, mcp_api_key):
|
|
357 |
2. If validation successful, call this function with the config_id
|
358 |
3. Monitoring will run automatically according to the validated schedule
|
359 |
|
360 |
-
|
361 |
- config_id: The ID from successful validate_api_configuration() execution (required)
|
362 |
- mcp_api_key: User's MCP API key for verification (must match validation step)
|
363 |
|
@@ -465,26 +483,6 @@ async def activate_monitoring(config_id, mcp_api_key):
|
|
465 |
print(
|
466 |
f"Executing API monitoring job for {name} at {now.isoformat()}. Next call at {next_call.isoformat()}"
|
467 |
)
|
468 |
-
# If the current time is past the stop time, do not execute the job but set is_active to False
|
469 |
-
if now > stop_at:
|
470 |
-
print(
|
471 |
-
f"Stopping API monitoring job for {name} as the stop time has been reached."
|
472 |
-
)
|
473 |
-
try:
|
474 |
-
job_conn = connect_to_db()
|
475 |
-
job_cur = job_conn.cursor()
|
476 |
-
job_cur.execute(
|
477 |
-
"""
|
478 |
-
UPDATE api_configurations SET is_active = %s WHERE config_id = %s
|
479 |
-
""",
|
480 |
-
(False, config_id),
|
481 |
-
)
|
482 |
-
job_conn.commit()
|
483 |
-
job_cur.close()
|
484 |
-
job_conn.close()
|
485 |
-
except Exception as db_exc:
|
486 |
-
print(f"Failed to update configuration status: {db_exc}")
|
487 |
-
return # Stop the job if the time has passed
|
488 |
|
489 |
try:
|
490 |
# Extract API configuration parameters
|
@@ -552,13 +550,32 @@ async def activate_monitoring(config_id, mcp_api_key):
|
|
552 |
job_cur = job_conn.cursor()
|
553 |
|
554 |
# Mark config as active (only once, on first run)
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
560 |
)
|
561 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
562 |
|
563 |
# Insert the actual API call result
|
564 |
job_cur.execute(
|
@@ -655,7 +672,9 @@ def retrieve_monitored_data(config_id, mcp_api_key, mode="summary"):
|
|
655 |
|
656 |
PREREQUISITE: Must call validate_api_configuration() first and obtain a config_id from successful validation, then activate_monitoring() to start monitoring.
|
657 |
|
658 |
-
This function can be called at any time after monitoring activation to retrieve the latest data collected by the monitoring system.
|
|
|
|
|
659 |
- config_id: The ID of the API configuration to retrieve data for (required)
|
660 |
- mcp_api_key: User's MCP API key for verification (must match validation step)
|
661 |
- mode: Data return mode - "summary" (LLM-optimized), "details" (full responses, minimal metadata), "full" (everything)
|
@@ -667,7 +686,9 @@ def retrieve_monitored_data(config_id, mcp_api_key, mode="summary"):
|
|
667 |
|
668 |
2. Retrieve data for weather alerts:
|
669 |
config_id: 987654321
|
670 |
-
mcp_api_key: "your_mcp_key_here"
|
|
|
|
|
671 |
- Dictionary with monitoring status in one of three formats based on mode parameter
|
672 |
|
673 |
SUMMARY mode (LLM-optimized, default):
|
@@ -957,6 +978,7 @@ import asyncio
|
|
957 |
|
958 |
|
959 |
async def main():
|
|
|
960 |
validation_response = validate_api_configuration(
|
961 |
mcp_api_key=os.getenv("MCP_API_KEY"),
|
962 |
name="Dog Facts API",
|
|
|
10 |
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
11 |
import requests
|
12 |
|
13 |
+
from apscheduler.schedulers.blocking import BlockingScheduler
|
14 |
|
15 |
# Load environment variables from .env file
|
16 |
load_dotenv(override=True)
|
|
|
26 |
raise ValueError(
|
27 |
"Database password not found in environment variables. Please set DB_PASSWORD."
|
28 |
)
|
29 |
+
|
30 |
+
# Get database connection details from environment variables with defaults
|
31 |
+
db_host = os.getenv("DB_HOST")
|
32 |
+
db_port = int(os.getenv("DB_PORT"))
|
33 |
+
db_name = os.getenv("DB_NAME")
|
34 |
+
db_user = os.getenv("DB_USER")
|
35 |
+
|
36 |
return psycopg2.connect(
|
37 |
+
host=db_host,
|
38 |
+
port=db_port,
|
39 |
+
database=db_name,
|
40 |
+
user=db_user,
|
41 |
password=db_password,
|
42 |
cursor_factory=psycopg2.extras.DictCursor,
|
43 |
)
|
|
|
97 |
additional_params,
|
98 |
schedule_interval_minutes,
|
99 |
stop_after_hours,
|
100 |
+
start_at, # IMPORTANT: Use empty string "" for immediate start (most common case)
|
101 |
):
|
102 |
"""
|
103 |
TOOL: Validate and store API configuration for monitoring.
|
|
|
109 |
before proceeding to activate_monitoring(). The API call may return success=True but contain
|
110 |
error messages (like "401 Unauthorized", "Invalid API key", etc.) in the sample_response.
|
111 |
|
112 |
+
CRITICAL: Always try to add parameters that will limit the API response to a manageable size.
|
113 |
+
|
114 |
+
CRITICAL: Be sure to always clearly inform the user of the config_id after a desired validation result.
|
115 |
+
|
116 |
WORKFLOW:
|
117 |
1. Call this function to validate API configuration
|
118 |
2. If success=False: Fix parameters and retry this function
|
|
|
120 |
4. If sample_response contains error messages: Fix API parameters and retry validation
|
121 |
5. If sample_response looks valid: Use config_id in activate_monitoring() to activate monitoring
|
122 |
|
123 |
+
ARGUMENTS:
|
124 |
- mcp_api_key: MCP API key serves as user identifier
|
125 |
- name: User-friendly name for the monitoring task
|
126 |
- description: Description of what is being monitored
|
|
|
129 |
- endpoint: The specific API endpoint
|
130 |
- param_keys_values: Parameter key-value pairs, one per line
|
131 |
- header_keys_values: Header key-value pairs, one per line
|
132 |
+
- additional_params: Optional JSON string for complex parameters
|
133 |
+
- schedule_interval_minutes: Minutes between calls
|
134 |
- stop_after_hours: Hours after which to stop (supports decimals, max 168 = 1 week)
|
135 |
+
- start_at: Optional datetime string for when to start the monitoring.
|
136 |
+
|
137 |
+
IMPORTANT: Leave as empty string "" for immediate start (most common use case, always default to this if no start time provided). Only provide a datetime string (e.g., "2024-06-15 09:00:00") if you need to schedule monitoring for a specific future time.
|
138 |
|
139 |
+
Input Examples:
|
140 |
+
|
141 |
+
1. Simple GET request to monitor stock price:
|
142 |
mcp_api_key: "your_mcp_key_here"
|
143 |
name: "NVDA Stock Price"
|
144 |
description: "Monitor NVIDIA stock price every 30 minutes"
|
|
|
152 |
stop_after_hours: 1.5
|
153 |
start_at: ""
|
154 |
|
155 |
+
2. Weather monitoring with free API:
|
156 |
mcp_api_key: "your_mcp_key_here"
|
157 |
+
name: "Weather Monitor"
|
158 |
+
description: "Monitor current weather conditions every 2 hours for one week using Open-Meteo free API"
|
159 |
+
method: "GET"
|
160 |
+
base_url: "https://api.open-meteo.com"
|
161 |
+
endpoint: "v1/forecast"
|
162 |
+
param_keys_values: "latitude: 40.7128\nlongitude: -74.0060\ncurrent: temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m\ntimezone: America/New_York"
|
163 |
+
header_keys_values: "Content-Type: application/json"
|
164 |
+
additional_params: "{}"
|
165 |
+
schedule_interval_minutes: 120
|
166 |
+
stop_after_hours: 168
|
167 |
start_at: "2024-06-15 09:00:00"
|
168 |
|
169 |
Returns:
|
|
|
177 |
"sample_response": {...},
|
178 |
"stop_at": "2025-06-11T12:00:00Z",
|
179 |
"start_at": "2025-06-04T12:00:00Z"
|
180 |
+
}
|
181 |
+
NEXT STEP: If success=True, call activate_monitoring(config_id, mcp_api_key) to activate monitoring
|
182 |
"""
|
183 |
try:
|
184 |
# Validate input parameters
|
|
|
375 |
2. If validation successful, call this function with the config_id
|
376 |
3. Monitoring will run automatically according to the validated schedule
|
377 |
|
378 |
+
ARGUMENTS:
|
379 |
- config_id: The ID from successful validate_api_configuration() execution (required)
|
380 |
- mcp_api_key: User's MCP API key for verification (must match validation step)
|
381 |
|
|
|
483 |
print(
|
484 |
f"Executing API monitoring job for {name} at {now.isoformat()}. Next call at {next_call.isoformat()}"
|
485 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
486 |
|
487 |
try:
|
488 |
# Extract API configuration parameters
|
|
|
550 |
job_cur = job_conn.cursor()
|
551 |
|
552 |
# Mark config as active (only once, on first run)
|
553 |
+
if not config["is_active"]:
|
554 |
+
job_cur.execute(
|
555 |
+
"""
|
556 |
+
UPDATE api_configurations SET is_active = %s WHERE config_id = %s
|
557 |
+
""",
|
558 |
+
(True, config_id),
|
559 |
+
)
|
560 |
+
print(f"Marked configuration {config_id} as active.")
|
561 |
+
|
562 |
+
# Check if this is the last call by comparing current time to stop_at
|
563 |
+
current_time = datetime.now()
|
564 |
+
next_call_time = current_time + timedelta(
|
565 |
+
minutes=schedule_interval_minutes
|
566 |
)
|
567 |
+
|
568 |
+
if next_call_time >= stop_at:
|
569 |
+
# This is the last call, mark as inactive
|
570 |
+
job_cur.execute(
|
571 |
+
"""
|
572 |
+
UPDATE api_configurations SET is_active = %s WHERE config_id = %s
|
573 |
+
""",
|
574 |
+
(False, config_id),
|
575 |
+
)
|
576 |
+
print(
|
577 |
+
f"Last call for configuration {config_id}. Marked as inactive."
|
578 |
+
)
|
579 |
|
580 |
# Insert the actual API call result
|
581 |
job_cur.execute(
|
|
|
672 |
|
673 |
PREREQUISITE: Must call validate_api_configuration() first and obtain a config_id from successful validation, then activate_monitoring() to start monitoring.
|
674 |
|
675 |
+
This function can be called at any time after monitoring activation to retrieve the latest data collected by the monitoring system.
|
676 |
+
|
677 |
+
ARGUMENTS:
|
678 |
- config_id: The ID of the API configuration to retrieve data for (required)
|
679 |
- mcp_api_key: User's MCP API key for verification (must match validation step)
|
680 |
- mode: Data return mode - "summary" (LLM-optimized), "details" (full responses, minimal metadata), "full" (everything)
|
|
|
686 |
|
687 |
2. Retrieve data for weather alerts:
|
688 |
config_id: 987654321
|
689 |
+
mcp_api_key: "your_mcp_key_here"
|
690 |
+
|
691 |
+
Returns:
|
692 |
- Dictionary with monitoring status in one of three formats based on mode parameter
|
693 |
|
694 |
SUMMARY mode (LLM-optimized, default):
|
|
|
978 |
|
979 |
|
980 |
async def main():
|
981 |
+
|
982 |
validation_response = validate_api_configuration(
|
983 |
mcp_api_key=os.getenv("MCP_API_KEY"),
|
984 |
name="Dog Facts API",
|
main.py → app.py
RENAMED
@@ -41,14 +41,14 @@ validation_tab = gr.Interface(
|
|
41 |
value="{}",
|
42 |
),
|
43 |
gr.Number(
|
44 |
-
label="Schedule Interval (minutes)", value=20, minimum=1, maximum=1440
|
45 |
),
|
46 |
gr.Number(
|
47 |
label="Stop After (hours)", value=24, minimum=0.1, maximum=168, step=0.1
|
48 |
),
|
49 |
gr.Textbox(
|
50 |
-
label="Start Time (optional)",
|
51 |
-
placeholder="YYYY-MM-DD HH:MM:SS
|
52 |
value="",
|
53 |
),
|
54 |
],
|
|
|
41 |
value="{}",
|
42 |
),
|
43 |
gr.Number(
|
44 |
+
label="Schedule Interval (minutes)", value=20, minimum=0.1, maximum=1440
|
45 |
),
|
46 |
gr.Number(
|
47 |
label="Stop After (hours)", value=24, minimum=0.1, maximum=168, step=0.1
|
48 |
),
|
49 |
gr.Textbox(
|
50 |
+
label="Start Time (optional - leave empty for immediate start)",
|
51 |
+
placeholder="Leave empty for immediate start, or enter YYYY-MM-DD HH:MM:SS for scheduled start",
|
52 |
value="",
|
53 |
),
|
54 |
],
|
requirements.txt
CHANGED
@@ -62,3 +62,4 @@ tzlocal==5.3.1
|
|
62 |
urllib3==2.4.0
|
63 |
uvicorn==0.34.3
|
64 |
websockets==15.0.1
|
|
|
|
62 |
urllib3==2.4.0
|
63 |
uvicorn==0.34.3
|
64 |
websockets==15.0.1
|
65 |
+
apscheduler==3.11.0
|