-
Notifications
You must be signed in to change notification settings - Fork 140
Feat: Integrate AI Price Optimisation for Smarter Pricing Strategies #126
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
Closed
Saahi30
wants to merge
56
commits into
AOSSIE-Org:main
from
Saahi30:ai-price-optimisation-integration
Closed
Changes from all commits
Commits
Show all changes
56 commits
Select commit
Hold shift + click to select a range
80baf01
feat: revamped ui
Saahi30 d3fe0ba
feat: added new tables
Saahi30 00c4703
feat: added fastapi models
Saahi30 0432220
feat: added pydantic schemas
Saahi30 ab357c6
feat: add api routes and minor security improvements
Saahi30 20e6bdd
feat: add smart features to searchbar
Saahi30 425c9ab
feat: added more endpoints for refinement
Saahi30 8c0f605
fix: add missing SponsorshipCreate import
Saahi30 635f252
fix: resolve CSS import order and TypeScript errors
Saahi30 d1b8b33
feat: connect frontend to backend with API integration and AI search
Saahi30 6f528ef
docs: add frontend-backend integration documentation
Saahi30 e62ba6b
fix: separate AI API service and fix 404 error for AI queries
Saahi30 d79c411
feat: improve AI response display with better formatting
Saahi30 34ede84
fix: remove the metric cards from brand dashboard
Saahi30 e36a966
fix: changed primary call model to kimi-k2
Saahi30 6658f61
fix: llm parsing inconsistency fix
Saahi30 6420e00
feat: add redis cloud connect
Saahi30 4a4382e
feat: add hybrid orchestration and session management
Saahi30 1cdee7c
fix: fix session management and response types
Saahi30 ce9e4d1
feat: chat component
Saahi30 7cc96af
feat: backend assistant integration
Saahi30 8c7cfad
feat: add dashboard route
Saahi30 0d833db
feat: add the brand overview dashboard
Saahi30 da4415b
feat: update dashboard overview for design
Saahi30 a11acd2
feat: Added contracts schemas
Saahi30 65fba45
feat: main Contracts dashboard
Saahi30 21e7896
feat: comprehensive contract details modal
Saahi30 cb8950a
built the remaining features
Saahi30 fd36d56
feat: add database queries for contracts
Saahi30 9823585
feat: create api system
Saahi30 80970cc
feat: register all endpoints and routing
Saahi30 3dc6902
fix: prevent startup failures by proper database connection
Saahi30 c58bb4f
fix: improved error handling
Saahi30 9cdc56a
feat: added social icons for use throughout the program
Saahi30 79ca174
feat: back to dashboard button
Saahi30 b5f3140
feat: core api build
Saahi30 8da532b
feat: management modals
Saahi30 738625f
feat: added genAI for smart contracts
Saahi30 d9dbdcb
feat: developed api routes for contracts
Saahi30 3cf4212
fix: resolve jsonb type casting in SQL
Saahi30 080e327
feat: add jurisdiction selection to contract generator
Saahi30 948eceb
feat: add jurisdiction support to contract generation API
Saahi30 02315f6
feat: add jurisdiction fields to edit modal
Saahi30 c101c62
fix: enhance contract update error handling
Saahi30 4527c1f
fix: improve API error handling for 5xx errors
Saahi30 af32739
feat: add pricing service and API routes
Saahi30 1d4f3b7
feat: add governing laws selector component
Saahi30 dd05553
feat: register pricing routes and update post API
Saahi30 4e28a7c
feat: integrate jurisdiction features in contract pages
Saahi30 efb958a
feat: update contract routes and templates with new generation logic
Saahi30 df6e2f2
feat: implement pricing service and test smart contract features
Saahi30 aed909c
feat: update contract modals with new UI components
Saahi30 1b826fc
feat: add backend exports functionality
Saahi30 167affd
chore(backend):services for data collection & ingestion
Saahi30 827aad6
feat(backend): ROI service & analytics endpoint
Saahi30 996562c
feat(frontend):visualize ROI analytics
Saahi30 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,244 @@ | ||
| from fastapi import APIRouter, HTTPException, Query, Depends, Request | ||
| from typing import Dict, Any, Optional | ||
| from pydantic import BaseModel | ||
| import logging | ||
| from ..services.ai_router import ai_router | ||
| from ..services.redis_client import get_session_state, save_session_state | ||
| import uuid | ||
|
|
||
| # Setup logging | ||
| logging.basicConfig(level=logging.INFO) | ||
| logger = logging.getLogger(__name__) | ||
|
|
||
| # Define Router | ||
| router = APIRouter(prefix="/api/ai", tags=["AI Query"]) | ||
|
|
||
| # Pydantic models for request/response | ||
| class AIQueryRequest(BaseModel): | ||
| query: str | ||
| brand_id: Optional[str] = None | ||
| context: Optional[Dict[str, Any]] = None | ||
|
|
||
| class AIQueryResponse(BaseModel): | ||
| intent: str | ||
| route: Optional[str] = None | ||
| parameters: Dict[str, Any] = {} | ||
| follow_up_needed: bool = False | ||
| follow_up_question: Optional[str] = None | ||
| explanation: str | ||
| original_query: str | ||
| timestamp: str | ||
|
|
||
| @router.post("/query", response_model=AIQueryResponse) | ||
| async def process_ai_query(request: AIQueryRequest, http_request: Request): | ||
| """ | ||
| Process a natural language query through AI and return routing information | ||
| """ | ||
| try: | ||
| # Validate input | ||
| if not request.query or len(request.query.strip()) == 0: | ||
| raise HTTPException(status_code=400, detail="Query cannot be empty") | ||
|
|
||
| # Process query through AI router | ||
| result = await ai_router.process_query( | ||
| query=request.query.strip(), | ||
| brand_id=request.brand_id | ||
| ) | ||
|
|
||
| # --- Hybrid Orchestration Logic --- | ||
| # Extended intent-to-parameter mapping for all available routes | ||
| intent_param_map = { | ||
| "dashboard_overview": {"required": ["brand_id"], "optional": []}, | ||
| "brand_profile": {"required": ["user_id"], "optional": []}, | ||
| "campaigns": {"required": ["brand_id"], "optional": ["campaign_id"]}, | ||
| "creator_matches": {"required": ["brand_id"], "optional": []}, | ||
| "creator_search": {"required": ["brand_id"], "optional": ["industry", "min_engagement", "location"]}, | ||
| "creator_profile": {"required": ["creator_id", "brand_id"], "optional": []}, | ||
| "analytics_performance": {"required": ["brand_id"], "optional": []}, | ||
| "analytics_revenue": {"required": ["brand_id"], "optional": []}, | ||
| "contracts": {"required": ["brand_id"], "optional": ["contract_id"]}, | ||
| } | ||
| intent = result.get("route") | ||
| params = result.get("parameters", {}) | ||
|
|
||
| # Debug: Log the parameters to understand the type issue | ||
| logger.info(f"Intent: {intent}") | ||
| logger.info(f"Params: {params}") | ||
| logger.info(f"Params type: {type(params)}") | ||
| for key, value in params.items(): | ||
| logger.info(f" {key}: {value} (type: {type(value)})") | ||
|
|
||
| api_result = None | ||
| api_error = None | ||
| # Prepare arguments for API calls, including optional params if present | ||
| def get_api_args(intent, params): | ||
| args = {} | ||
| if intent in intent_param_map: | ||
| # Add required params | ||
| for param in intent_param_map[intent]["required"]: | ||
| if params.get(param) is not None: | ||
| args[param] = params[param] | ||
| # Add optional params if present | ||
| for param in intent_param_map[intent]["optional"]: | ||
| if params.get(param) is not None: | ||
| args[param] = params[param] | ||
| return args | ||
|
|
||
| # Check if all required params are present | ||
| all_params_present = True | ||
| missing_params = [] | ||
| if intent in intent_param_map: | ||
| for param in intent_param_map[intent]["required"]: | ||
| if not params.get(param): | ||
| all_params_present = False | ||
| missing_params.append(param) | ||
|
|
||
| # Allow queries with only optional params if API supports it (e.g., creator_search with filters) | ||
| only_optional_params = False | ||
| if intent in intent_param_map and not all_params_present: | ||
| # If at least one optional param is present and no required params are present | ||
| if ( | ||
| len(intent_param_map[intent]["optional"]) > 0 and | ||
| all(params.get(p) is None for p in intent_param_map[intent]["required"]) and | ||
| any(params.get(p) is not None for p in intent_param_map[intent]["optional"]) | ||
| ): | ||
| only_optional_params = True | ||
|
|
||
| if (intent and all_params_present) or (intent and only_optional_params): | ||
| try: | ||
| api_args = get_api_args(intent, params) | ||
| # Use aliases for get_campaigns and get_contracts | ||
| if intent == "creator_search": | ||
| from ..routes.brand_dashboard import search_creators | ||
| api_result = await search_creators(**api_args) | ||
| elif intent == "dashboard_overview": | ||
| from ..routes.brand_dashboard import get_dashboard_overview | ||
| api_result = await get_dashboard_overview(**api_args) | ||
| elif intent == "creator_matches": | ||
| from ..routes.brand_dashboard import get_creator_matches | ||
| api_result = await get_creator_matches(**api_args) | ||
| elif intent == "brand_profile": | ||
| from ..routes.brand_dashboard import get_brand_profile | ||
| api_result = await get_brand_profile(**api_args) | ||
| elif intent == "campaigns": | ||
| from ..routes.brand_dashboard import get_brand_campaigns as get_campaigns | ||
| api_result = await get_campaigns(**api_args) | ||
| elif intent == "creator_profile": | ||
| from ..routes.brand_dashboard import get_creator_profile | ||
| api_result = await get_creator_profile(**api_args) | ||
| elif intent == "analytics_performance": | ||
| from ..routes.brand_dashboard import get_campaign_performance | ||
| api_result = await get_campaign_performance(**api_args) | ||
| elif intent == "analytics_revenue": | ||
| from ..routes.brand_dashboard import get_revenue_analytics | ||
| api_result = await get_revenue_analytics(**api_args) | ||
| elif intent == "contracts": | ||
| from ..routes.brand_dashboard import get_brand_contracts as get_contracts | ||
| api_result = await get_contracts(**api_args) | ||
| except Exception as api_exc: | ||
| logger.error(f"API call failed for intent '{intent}': {api_exc}") | ||
| api_error = str(api_exc) | ||
|
|
||
| # Convert to response model, add 'result' field for actual data | ||
| response = AIQueryResponse( | ||
| intent=result.get("intent", "unknown"), | ||
| route=result.get("route"), | ||
| parameters=params, | ||
| follow_up_needed=not all_params_present and not only_optional_params or api_error is not None, | ||
| follow_up_question=(result.get("follow_up_question") if not all_params_present and not only_optional_params else None), | ||
| explanation=(result.get("explanation", "") if not api_error else f"An error occurred while processing your request: {api_error}"), | ||
| original_query=result.get("original_query", request.query), | ||
| timestamp=result.get("timestamp", ""), | ||
| ) | ||
| # Attach result if available | ||
| response_dict = response.dict() | ||
| # 1. Get or generate session_id | ||
| session_id = http_request.headers.get("X-Session-ID") | ||
| if not session_id and request.context: | ||
| session_id = request.context.get("session_id") | ||
| if not session_id: | ||
| session_id = str(uuid.uuid4()) | ||
|
|
||
| # 2. Load previous state from Redis | ||
| state = await get_session_state(session_id) | ||
| prev_params = state.get("params", {}) | ||
| prev_intent = state.get("intent") | ||
|
|
||
| # 3. Merge new params and intent | ||
| # Use new intent if present, else previous | ||
| intent = result.get("route") or prev_intent | ||
| params = {**prev_params, **result.get("parameters", {})} | ||
| state["params"] = params | ||
| state["intent"] = intent | ||
|
|
||
| # 4. Save updated state to Redis | ||
| await save_session_state(session_id, state) | ||
|
|
||
| response_dict["session_id"] = session_id | ||
| if api_result is not None: | ||
| response_dict["result"] = api_result | ||
| if api_error is not None: | ||
| response_dict["error"] = api_error | ||
| return response_dict | ||
| except HTTPException: | ||
| raise | ||
| except Exception as e: | ||
| logger.error(f"Error processing AI query: {e}") | ||
| raise HTTPException(status_code=500, detail="Failed to process AI query") | ||
|
|
||
| @router.get("/routes") | ||
| async def get_available_routes(): | ||
| """ | ||
| Get list of available routes that the AI can route to | ||
| """ | ||
| try: | ||
| routes = ai_router.list_available_routes() | ||
| return { | ||
| "available_routes": routes, | ||
| "total_routes": len(routes) | ||
| } | ||
| except Exception as e: | ||
| logger.error(f"Error fetching available routes: {e}") | ||
| raise HTTPException(status_code=500, detail="Failed to fetch routes") | ||
|
|
||
| @router.get("/route/{route_name}") | ||
| async def get_route_info(route_name: str): | ||
| """ | ||
| Get detailed information about a specific route | ||
| """ | ||
| try: | ||
| route_info = ai_router.get_route_info(route_name) | ||
| if not route_info: | ||
| raise HTTPException(status_code=404, detail=f"Route '{route_name}' not found") | ||
|
|
||
| return { | ||
| "route_name": route_name, | ||
| "info": route_info | ||
| } | ||
| except HTTPException: | ||
| raise | ||
| except Exception as e: | ||
| logger.error(f"Error fetching route info: {e}") | ||
| raise HTTPException(status_code=500, detail="Failed to fetch route info") | ||
|
|
||
| @router.post("/test") | ||
| async def test_ai_query(query: str = Query(..., description="Test query")): | ||
| """ | ||
| Test endpoint for AI query processing (for development) | ||
| """ | ||
| try: | ||
| # Process test query | ||
| result = await ai_router.process_query(query=query) | ||
|
|
||
| return { | ||
| "test_query": query, | ||
| "result": result, | ||
| "status": "success" | ||
| } | ||
| except Exception as e: | ||
| logger.error(f"Error in test AI query: {e}") | ||
| return { | ||
| "test_query": query, | ||
| "error": str(e), | ||
| "status": "error" | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Response model is dropping session_id/result/error. Include them in schema and avoid mutable default.
FastAPI filters unknown fields; your extras wonβt reach clients. Also avoid
{}as default.π€ Prompt for AI Agents