From 49044bb7fa6bcf840e7738184cf6e51f8dc97c27 Mon Sep 17 00:00:00 2001 From: aslon Date: Fri, 21 Nov 2025 00:25:39 -0500 Subject: [PATCH 1/2] some changes --- functions_to_format/functions/functions.py | 39 +- .../functions/general/__init__.py | 1 + pyproject.toml | 2 +- src/server.py | 6 +- src/ui_router.py | 30 -- telemetry/ARCHITECTURE.md | 374 ------------------ telemetry/METRICS_PREFIX_CHANGES.md | 124 ------ 7 files changed, 9 insertions(+), 567 deletions(-) delete mode 100644 src/ui_router.py delete mode 100644 telemetry/ARCHITECTURE.md delete mode 100644 telemetry/METRICS_PREFIX_CHANGES.md diff --git a/functions_to_format/functions/functions.py b/functions_to_format/functions/functions.py index f59a2ea..129bf5a 100644 --- a/functions_to_format/functions/functions.py +++ b/functions_to_format/functions/functions.py @@ -57,10 +57,7 @@ def chatbot_answer(context: Context) -> BuildOutput: return output -def unauthorized_response(context): - # Janis Rubins: logic unchanged, just returns text_widget schema and llm_output as data - from models.context import Context - +def unauthorized_response(context: Context): # Extract values from context llm_output = context.llm_output backend_output = context.backend_output @@ -151,9 +148,7 @@ def build_contacts_list_widget(contacts: list[Contact]) -> Dict[str, Any]: return container -def build_contacts_list(context) -> BuildOutput: - from models.context import Context - +def build_contacts_list(context: Context) -> BuildOutput: # Extract values from context llm_output = context.llm_output backend_output = context.backend_output @@ -217,33 +212,3 @@ def build_contacts_list(context) -> BuildOutput: ) save_builder_output(context, output) return output - - -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######################################################## -######### LEGACY UNUSED CODE ######### diff --git a/functions_to_format/functions/general/__init__.py b/functions_to_format/functions/general/__init__.py index dab58fe..e86e268 100644 --- a/functions_to_format/functions/general/__init__.py +++ b/functions_to_format/functions/general/__init__.py @@ -26,6 +26,7 @@ def add_ui_to_widget( widget_args = widget_input.args widget_input.widget.build_ui(sdui_function, **widget_args) except Exception as e: + logger.error("Error building widget", error=e) logger.exception("Error building widget", error=e) continue widgets: List[Widget] = [] diff --git a/pyproject.toml b/pyproject.toml index 1c6b6de..f53d3b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ui-server" -version = "1.001" +version = "1.01" description = "UI Server to generate UI json representations using Yandex Divkit" readme = "README.md" requires-python = ">=3.13" diff --git a/src/server.py b/src/server.py index 7d3e08e..da8cc50 100644 --- a/src/server.py +++ b/src/server.py @@ -23,7 +23,11 @@ ) try: - version = open("version.txt", "r").read() + import tomllib + + with open("pyproject.toml", "rb") as f: + pyproject_data = tomllib.load(f) + version = pyproject_data.get("project", {}).get("version", "0") except Exception as e: version = "0" finally: diff --git a/src/ui_router.py b/src/ui_router.py deleted file mode 100644 index bfc899c..0000000 --- a/src/ui_router.py +++ /dev/null @@ -1,30 +0,0 @@ -from fastapi import APIRouter, Request, Response -from typing import Dict, Any - -# Initialize router -router = APIRouter(prefix="/ui", tags=["ui"]) - - -# Define routes -@router.get("/build/{config_id}") -async def build_ui_from_config(request: Request, config_id: str): - """Build UI from a specific configuration""" - pass - - -@router.post("/render") -async def render_ui_component(request: Request): - """Render a specific UI component with provided data""" - pass - - -@router.get("/templates") -async def get_ui_templates(request: Request): - """Get available UI templates""" - pass - - -@router.post("/preview") -async def preview_ui(request: Request): - """Generate a UI preview from provided configuration""" - pass diff --git a/telemetry/ARCHITECTURE.md b/telemetry/ARCHITECTURE.md deleted file mode 100644 index 22f320a..0000000 --- a/telemetry/ARCHITECTURE.md +++ /dev/null @@ -1,374 +0,0 @@ -# Telemetry Architecture - -This document describes the architecture and data flow of the UI Server telemetry system. - -## System Architecture - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ UI Server │ -│ │ -│ ┌──────────────────────────────────────────────────────────┐ │ -│ │ FastAPI Application │ │ -│ │ │ │ -│ │ ┌──────────────────────────────────────────────────┐ │ │ -│ │ │ TelemetryMiddleware │ │ │ -│ │ │ • Intercepts all HTTP requests │ │ │ -│ │ │ • Creates span for each request │ │ │ -│ │ │ • Adds HTTP attributes │ │ │ -│ │ │ • Measures duration │ │ │ -│ │ │ • Records exceptions │ │ │ -│ │ └──────────────────────────────────────────────────┘ │ │ -│ │ ↓ │ │ -│ │ ┌──────────────────────────────────────────────────┐ │ │ -│ │ │ Endpoint Handlers │ │ │ -│ │ │ • /chat/v3/build_ui │ │ │ -│ │ │ • /chat/v2/build_ui/actions │ │ │ -│ │ │ • Manual span creation │ │ │ -│ │ │ • Function metrics recording │ │ │ -│ │ └──────────────────────────────────────────────────┘ │ │ -│ │ ↓ │ │ -│ │ ┌──────────────────────────────────────────────────┐ │ │ -│ │ │ Business Logic (functions_mapper) │ │ │ -│ │ │ • User code with @trace_function decorators │ │ │ -│ │ │ • Manual tracer.start_as_current_span() │ │ │ -│ │ │ • Custom attributes and metrics │ │ │ -│ │ └──────────────────────────────────────────────────┘ │ │ -│ └────────────────────────────────────────────────────────┘ │ -│ │ -│ ┌────────────────────┐ ┌──────────────────────────────┐ │ -│ │ MetricsCollector │ │ OpenTelemetry Tracer │ │ -│ │ • Request metrics │ │ • Span creation │ │ -│ │ • Function metrics│ │ • Context propagation │ │ -│ │ • Custom metrics │ │ • Attribute management │ │ -│ └────────────────────┘ └──────────────────────────────┘ │ -│ ↓ ↓ │ -│ ┌────────────────────────────────────────────────────────┐ │ -│ │ OpenTelemetry SDK │ │ -│ │ • BatchSpanProcessor │ │ -│ │ • PeriodicExportingMetricReader │ │ -│ │ • Resource management │ │ -│ └────────────────────────────────────────────────────────┘ │ -│ ↓ ↓ │ -│ ┌────────────────────┐ ┌──────────────────────────────┐ │ -│ │ OTLP Exporter │ │ Console Exporter (dev) │ │ -│ │ (Production) │ │ (Development) │ │ -│ └────────────────────┘ └──────────────────────────────┘ │ -└───────────────┬──────────────────────┬───────────────────────┘ - │ │ - ↓ ↓ - ┌─────────────────────┐ ┌──────────────────┐ - │ Observability │ │ Console Output │ - │ Platform │ │ (stdout/stderr) │ - │ │ └──────────────────┘ - │ • Jaeger │ - │ • Grafana Cloud │ - │ • New Relic │ - │ • Honeycomb │ - │ • Datadog │ - │ • AWS X-Ray │ - └─────────────────────┘ -``` - -## Data Flow - -### Request Processing Flow - -``` -1. HTTP Request arrives - ↓ -2. TelemetryMiddleware intercepts - ↓ -3. Create parent span: "GET /path" - ↓ -4. Add HTTP attributes (method, path, client_ip, etc.) - ↓ -5. FastAPI routes to endpoint handler - ↓ -6. Endpoint creates child span: "build_ui_v3" - ↓ -7. Add function attributes (name, version, language) - ↓ -8. Call business logic from functions_mapper - ↓ -9. [Optional] Business logic creates more child spans - ↓ -10. Function completes, record duration - ↓ -11. MetricsCollector records metrics - ↓ -12. Return response - ↓ -13. TelemetryMiddleware adds response attributes - ↓ -14. Span completed with final duration - ↓ -15. BatchSpanProcessor batches span - ↓ -16. OTLP Exporter sends to observability platform -``` - -### Span Hierarchy Example - -``` -HTTP Request Span (root) -└─ build_ui_v3 (child) - ├─ fetch_user_data (child) - ├─ process_data (child) - │ ├─ validate_input (grandchild) - │ └─ transform_data (grandchild) - └─ save_results (child) -``` - -## Component Details - -### 1. Setup Module (`setup.py`) - -**Responsibilities:** -- Initialize OpenTelemetry SDK -- Configure trace provider and meter provider -- Set up exporters (OTLP, Console) -- Manage resource attributes -- Provide global tracer and meter instances - -**Key Functions:** -- `setup_telemetry(app, service_name, version)` - Initialize everything -- `get_tracer()` - Get global tracer instance -- `get_meter()` - Get global meter instance - -### 2. Middleware (`middleware.py`) - -**Responsibilities:** -- Intercept all HTTP requests -- Create and manage request spans -- Record HTTP request metrics via MetricsCollector -- Add HTTP-specific attributes -- Measure request duration -- Track active requests -- Handle exceptions gracefully - -**Attributes Added:** -- `http.method` - HTTP method -- `http.url` - Full URL -- `http.path` - Request path -- `http.status_code` - Response status -- `http.duration_ms` - Request duration -- `http.client_ip` - Client IP address -- `app.language` - Request language header - -**Metrics Recorded:** -- All HTTP request metrics through MetricsCollector -- Ensures all metrics have `ui_server` prefix - -### 3. Metrics Collector (`metrics.py`) - -**Responsibilities:** -- Define standard metrics -- Record request metrics -- Record function invocation metrics -- Support custom business metrics -- Track active requests - -**Metrics Provided:** -- `ui_server.requests.total` - Counter -- `ui_server.requests.duration` - Histogram -- `ui_server.requests.active` - UpDownCounter -- `ui_server.errors.total` - Counter -- `ui_server.functions.invocations` - Counter -- `ui_server.functions.duration` - Histogram -- `ui_server.functions.errors` - Counter -- `ui_server.requests.language` - Counter - -### 4. Decorators (`decorators.py`) - -**Responsibilities:** -- Provide easy-to-use function tracing -- Support both sync and async functions -- Add function metadata to spans -- Handle exceptions properly - -**Decorators Provided:** -- `@trace_function(span_name=None)` - Trace any function -- `@trace_method` - Trace class methods - -## Configuration - -### Environment Variables - -| Variable | Purpose | Default | Example | -|----------|---------|---------|---------| -| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP endpoint URL | None | `http://localhost:4317` | -| `OTEL_EXPORTER_OTLP_HEADERS` | Authentication headers | None | `authorization=Bearer TOKEN` | -| `OTEL_CONSOLE_EXPORT` | Enable console export | `false` | `true` | -| `ENVIRONMENT` | Deployment environment | `development` | `production` | - -### Resource Attributes - -Automatically added to all spans and metrics: -- `service.name` - Service name (ui_server) -- `service.version` - Service version (from version.txt) -- `deployment.environment` - Environment (development/production) - -## Export Strategies - -### Development Mode -``` -TracerProvider -└─ BatchSpanProcessor - └─ ConsoleSpanExporter → stdout - -MeterProvider -└─ PeriodicExportingMetricReader (60s) - └─ ConsoleMetricExporter → stdout -``` - -### Production Mode -``` -TracerProvider -└─ BatchSpanProcessor - └─ OTLPSpanExporter → OTLP Endpoint - -MeterProvider -└─ PeriodicExportingMetricReader (60s) - └─ OTLPMetricExporter → OTLP Endpoint -``` - -### Hybrid Mode (Both) -``` -TracerProvider -├─ BatchSpanProcessor → OTLPSpanExporter -└─ BatchSpanProcessor → ConsoleSpanExporter - -MeterProvider -├─ PeriodicExportingMetricReader → OTLPMetricExporter -└─ PeriodicExportingMetricReader → ConsoleMetricExporter -``` - -## Performance Considerations - -### Batching -- Spans are batched before export -- Default batch size: 512 spans -- Max queue size: 2048 spans -- Export interval: 5 seconds - -### Async Export -- All exports happen in background threads -- No blocking of main application -- Failed exports are retried - -### Sampling (Future) -- Can implement sampling for high-volume endpoints -- TraceIDRatioBased sampler -- ParentBased sampler - -### Resource Usage -- Minimal CPU overhead (~1-2%) -- Memory scales with request volume -- Network bandwidth depends on trace volume - -## Error Handling - -### Graceful Degradation -- If OTLP endpoint is unavailable, traces are dropped -- Console exporter always works (no network dependency) -- Application continues to function normally - -### Exception Recording -- Exceptions are automatically recorded in spans -- Stack traces are included -- Error status is set on span - -## Security Considerations - -### Data Protection -- Sensitive data should NOT be added to span attributes -- Review all custom attributes -- Use environment variables for credentials - -### Authentication -- OTLP headers can include authentication -- Support for Bearer tokens, API keys -- TLS/SSL for encrypted transmission - -## Future Enhancements - -### Planned Features -1. **Prometheus Metrics Export** - - Add `/metrics` endpoint - - Export metrics in Prometheus format - -2. **Automatic Sampling** - - Sample high-volume endpoints - - Keep all error traces - -3. **Custom Dashboard Templates** - - Pre-built Grafana dashboards - - Alert rule templates - -4. **Distributed Context Propagation** - - Propagate trace context to external services - - Support W3C Trace Context standard - -5. **Log Correlation** - - Add trace IDs to log messages - - Link traces to logs in observability platforms - -## Integration Examples - -### Adding Tracing to a New Endpoint - -```python -@app.post("/new/endpoint") -async def new_endpoint(request: Request, data: InputData): - with tracer.start_as_current_span("new_endpoint_operation") as span: - # Add attributes - span.set_attribute("data.type", data.type) - span.set_attribute("data.size", len(data.items)) - - # Your logic here - result = process_data(data) - - # Record metrics - metrics_collector.record_custom_metric( - "new_endpoint_calls", - 1, - {"status": "success"} - ) - - return result -``` - -### Adding Tracing to Business Logic - -```python -@trace_function("process_user_request") -async def process_user_request(user_id: str, request_data: dict): - # Automatically traced - # Add custom attributes in the function - tracer = get_tracer() - span = trace.get_current_span() - span.set_attribute("user_id", user_id) - - # Your logic here - return result -``` - -## Monitoring the Telemetry System - -### Health Checks -- Monitor OTLP exporter success rate -- Check span queue size -- Monitor export latency - -### Metrics to Watch -- `otel.exporter.spans.exported` - Exported span count -- `otel.exporter.spans.dropped` - Dropped span count -- `otel.processor.queue_size` - Current queue size - -## References - -- [OpenTelemetry Specification](https://opentelemetry.io/docs/specs/otel/) -- [Python SDK Documentation](https://opentelemetry-python.readthedocs.io/) -- [OTLP Protocol](https://opentelemetry.io/docs/specs/otlp/) - diff --git a/telemetry/METRICS_PREFIX_CHANGES.md b/telemetry/METRICS_PREFIX_CHANGES.md deleted file mode 100644 index 131820e..0000000 --- a/telemetry/METRICS_PREFIX_CHANGES.md +++ /dev/null @@ -1,124 +0,0 @@ -# Metrics Prefix Changes Summary - -## Overview -All telemetry metrics exported by the UI Server now start with the `ui_server` prefix to distinguish them from metrics of other services. - -## Changes Made - -### 1. Modified `telemetry/setup.py` -**Change**: Disabled automatic metrics from FastAPIInstrumentor - -**Reason**: FastAPIInstrumentor automatically creates HTTP metrics with standard OpenTelemetry names (like `http.server.duration`, `http.server.active_requests`) that don't have the `ui_server` prefix. By disabling its metrics and using our custom MetricsCollector, we ensure all metrics have the proper prefix. - -**Implementation**: -```python -FastAPIInstrumentor.instrument_app( - app, - excluded_urls="/health,/metrics", # Don't trace health/metrics endpoints - meter_provider=None, # Disable automatic metrics (we use MetricsCollector instead) -) -``` - -### 2. Enhanced `telemetry/middleware.py` -**Change**: Integrated MetricsCollector into TelemetryMiddleware - -**Reason**: The middleware now not only creates traces but also records metrics through MetricsCollector, which uses the `ui_server` prefix for all metrics. - -**Key improvements**: -- Added `metrics_collector` parameter to constructor -- Records HTTP request metrics with `ui_server` prefix -- Tracks active requests counter -- Records request duration, status codes, and error rates -- All metrics go through MetricsCollector which has proper prefixes - -### 3. Updated `src/server.py` -**Change**: Pass MetricsCollector instance to TelemetryMiddleware - -**Implementation**: -```python -# Initialize metrics collector first -metrics_collector = MetricsCollector() - -# Pass it to the middleware -app.add_middleware(TelemetryMiddleware, metrics_collector=metrics_collector) -``` - -## All Exported Metrics (with ui_server prefix) - -### HTTP Request Metrics -- `ui_server.requests.total` - Total number of HTTP requests -- `ui_server.requests.duration` - Request duration histogram (milliseconds) -- `ui_server.requests.active` - Number of currently active requests -- `ui_server.errors.total` - Total number of HTTP errors (status >= 400) -- `ui_server.requests.language` - Request count by language - -### Function Invocation Metrics -- `ui_server.functions.invocations` - Total function invocations -- `ui_server.functions.duration` - Function execution duration (milliseconds) -- `ui_server.functions.errors` - Total function errors - -### Custom Metrics -- `ui_server.custom.{metric_name}` - Any custom metrics recorded via `record_custom_metric()` - -## Metric Labels/Attributes - -### Request Metrics Attributes -- `http.method` - HTTP method (GET, POST, etc.) -- `http.path` - Request path -- `http.status_code` - HTTP status code -- `language` - Request language (when provided) - -### Function Metrics Attributes -- `function.name` - Name of the function invoked -- `function.version` - API version (v2, v3) -- `function.success` - Whether execution was successful - -## Prometheus Format - -When exported to Prometheus, metric names are converted: -- Dots (`.`) are replaced with underscores (`_`) -- Example: `ui_server.requests.total` → `ui_server_requests_total` - -The Grafana dashboard (`grafana-dashboard.json`) is already configured to use these Prometheus-formatted names. - -## Verification - -To verify all metrics have the correct prefix: - -1. Start the server -2. Make some requests -3. Access the `/metrics` endpoint -4. All metrics should start with `ui_server_` (Prometheus format) - -Example output: -``` -# HELP ui_server_requests_total Total number of requests -# TYPE ui_server_requests_total counter -ui_server_requests_total{http_method="GET",http_path="/health",http_status_code="200"} 5.0 - -# HELP ui_server_requests_duration Request duration in milliseconds -# TYPE ui_server_requests_duration histogram -ui_server_requests_duration_bucket{http_method="POST",http_path="/chat/v3/build_ui",http_status_code="200",le="10.0"} 2.0 -... -``` - -## Impact - -### Before Changes -- FastAPIInstrumentor created automatic metrics: `http.server.duration`, `http.server.active_requests` -- Custom metrics had `ui_server` prefix -- Mixed metric naming made it hard to filter by service - -### After Changes -- **All metrics** exported start with `ui_server` prefix -- Easy to filter and query metrics for this service specifically -- No confusion with other services' metrics -- Consistent naming across all metrics - -## No Breaking Changes - -- The Grafana dashboard already uses the correct metric names -- Existing metric queries will continue to work -- All metric names that previously had `ui_server` prefix remain unchanged -- Only removed the automatic FastAPI metrics that didn't have the prefix - From 957e8393ab806ee9ddb3046a46c370f1d9733a83 Mon Sep 17 00:00:00 2001 From: aslon Date: Sun, 28 Dec 2025 23:27:51 -0500 Subject: [PATCH 2/2] payments of home utilities fix --- functions_to_format/functions/__init__.py | 2 + .../functions/general/utils.py | 5 +- functions_to_format/functions/payment.py | 68 ++++++++++++------- utils/show_logs.sh | 50 ++++++++++++++ 4 files changed, 98 insertions(+), 27 deletions(-) create mode 100644 utils/show_logs.sh diff --git a/functions_to_format/functions/__init__.py b/functions_to_format/functions/__init__.py index e650872..8865687 100644 --- a/functions_to_format/functions/__init__.py +++ b/functions_to_format/functions/__init__.py @@ -8,6 +8,7 @@ get_number_by_receiver_name, send_money_to_someone_via_card, pay_for_home_utility, + get_home_utility_suppliers, ) from .contact import build_contact_widget, get_contact from .news import build_news_widget, get_news @@ -54,4 +55,5 @@ "get_home_balances": get_home_balances, "build_contacts_list": build_contacts_list, "pay_for_home_utility": pay_for_home_utility, + "get_home_utility_suppliers": get_home_utility_suppliers, } diff --git a/functions_to_format/functions/general/utils.py b/functions_to_format/functions/general/utils.py index b867012..171066f 100644 --- a/functions_to_format/functions/general/utils.py +++ b/functions_to_format/functions/general/utils.py @@ -52,10 +52,13 @@ async def upload_usages_async() -> None: config.mongo.database_name # pyright: ignore[reportOptionalMemberAccess] ).get_collection(config.mongo.collection_name) # pyright: ignore[reportOptionalMemberAccess] tasks = [] - for file in os.listdir("logs/usage"): + files = os.listdir("logs/usage") + for file in files: tasks.append(load_usages_async(collection, f"logs/usage/{file}")) await asyncio.gather(*tasks) + for file in files: + os.remove(f"logs/usage/{file}") except Exception as e: logger.error(f"Error uploading usages: {e}") finally: diff --git a/functions_to_format/functions/payment.py b/functions_to_format/functions/payment.py index 408a3a1..b19f862 100644 --- a/functions_to_format/functions/payment.py +++ b/functions_to_format/functions/payment.py @@ -1,5 +1,7 @@ import json +from functions_to_format.functions.functions import chatbot_answer + from .general import ( add_ui_to_widget, Widget, @@ -304,36 +306,46 @@ def pay_for_home_utility(context: Context) -> BuildOutput: api_key = context.api_key logger = context.logger_context.logger - backend_output = PaymentManagerPaymentResponse.model_validate(backend_output) + inp = {} - logger.info("pay_for_home_utility") + try: + backend_output = PaymentManagerPaymentResponse.model_validate(backend_output) + payment_status_widget = Widget( + order=2, + type="payment_status_widget", + name="payment_status_widget", + layout="vertical", + fields=["payment_status"], + values=[backend_output.model_dump()], + ) + inp[build_pay_for_home_utility_ui] = WidgetInput( + widget=payment_status_widget, + args={"payment_response": backend_output}, + ) - text_widget = TextWidget( - order=1, - values=[{"text": llm_output}], - ) - payment_status_widget = Widget( - order=2, - type="payment_status_widget", - name="payment_status_widget", - layout="vertical", - fields=["payment_status"], - values=[backend_output.model_dump()], - ) + logger.info("pay_for_home_utility") + + text_widget = TextWidget( + order=1, + values=[{"text": llm_output}], + ) + inp[build_text_widget] = WidgetInput( + widget=text_widget, + args={ + "text": llm_output, + }, + ) + + except Exception as e: + inp[build_text_widget] = WidgetInput( + widget=text_widget, + args={ + "text": llm_output, + }, + ) widgets = add_ui_to_widget( - { - build_text_widget: WidgetInput( - widget=text_widget, - args={ - "text": llm_output, - }, - ), - build_pay_for_home_utility_ui: WidgetInput( - widget=payment_status_widget, - args={"payment_response": backend_output}, - ), - }, + inp, version, ) output = BuildOutput( @@ -1103,6 +1115,10 @@ def get_fields_of_supplier_ui(fields: List[Field]): return div +def get_home_utility_suppliers(context) -> BuildOutput: + return chatbot_answer(context) + + if __name__ == "__main__": # check get_contacts with action first # output = get_receiver_id_by_receiver_phone_number( diff --git a/utils/show_logs.sh b/utils/show_logs.sh new file mode 100644 index 0000000..84f2176 --- /dev/null +++ b/utils/show_logs.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Get list of running containers +echo "Available Docker containers:" +containers=$(docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}") +echo "$containers" + +# Get just the container names for selection +container_names=$(docker ps --format "{{.Names}}") + +if [ -z "$container_names" ]; then + echo "No running containers found." + exit 1 +fi + +echo "" +echo "Available containers:" +# Create numbered list of containers +container_array=() +counter=1 +while IFS= read -r container; do + echo "$counter) $container" + container_array+=("$container") + ((counter++)) +done <<< "$container_names" + +echo "" +read -p "Enter the container name or number from the list: " selection + +# Check if selection is a number +if [[ "$selection" =~ ^[0-9]+$ ]]; then + # Selection is a number + if [ "$selection" -ge 1 ] && [ "$selection" -le "${#container_array[@]}" ]; then + selected_container="${container_array[$((selection-1))]}" + else + echo "Error: Invalid number. Please select a number between 1 and ${#container_array[@]}." + exit 1 + fi +else + # Selection is a container name + selected_container="$selection" + # Validate that the selected container exists + if ! echo "$container_names" | grep -q "^$selected_container$"; then + echo "Error: Container '$selected_container' not found in running containers." + exit 1 + fi +fi + +# Show logs for the selected container +docker logs -f --tail 100 "$selected_container" \ No newline at end of file