MuhammadSaad16 commited on
Commit
24b4e0b
·
1 Parent(s): 8770644

change API Key

Browse files
app.log CHANGED
@@ -634,3 +634,178 @@ openai.APIConnectionError: Connection error.
634
  2025-12-15 17:12:31,878 - httpx - INFO - HTTP Request: POST https://generativelanguage.googleapis.com/v1beta/openai/chat/completions "HTTP/1.1 200 OK"
635
  2025-12-15 17:12:32,176 - httpx - INFO - HTTP Request: POST https://generativelanguage.googleapis.com/v1beta/openai/chat/completions "HTTP/1.1 200 OK"
636
  2025-12-15 17:12:32,178 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
634
  2025-12-15 17:12:31,878 - httpx - INFO - HTTP Request: POST https://generativelanguage.googleapis.com/v1beta/openai/chat/completions "HTTP/1.1 200 OK"
635
  2025-12-15 17:12:32,176 - httpx - INFO - HTTP Request: POST https://generativelanguage.googleapis.com/v1beta/openai/chat/completions "HTTP/1.1 200 OK"
636
  2025-12-15 17:12:32,178 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
637
+ 2025-12-16 19:07:13,495 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
638
+ 2025-12-16 19:31:38,503 - app - INFO - Created new session for streaming chat: b88727b0-fd92-4a19-9af3-85d13a57fac3
639
+ 2025-12-16 19:31:38,505 - app - INFO - Stream request from 127.0.0.1: language=english, message=hi..., session_id=b88727b0-fd92-4a19-9af3-85d13a57fac3
640
+ 2025-12-16 19:31:39,919 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='hi', language='english', session_id='b88727b0-fd92-4a19-9af3-85d13a57fac3'
641
+ 2025-12-16 19:31:40,250 - app - INFO - Retrieved 1 history messages for session b88727b0-fd92-4a19-9af3-85d13a57fac3
642
+ 2025-12-16 19:32:06,502 - httpx - INFO - HTTP Request: POST https://generativelanguage.googleapis.com/v1beta/openai/chat/completions "HTTP/1.1 200 OK"
643
+ 2025-12-16 19:32:06,830 - httpx - INFO - HTTP Request: POST https://generativelanguage.googleapis.com/v1beta/openai/chat/completions "HTTP/1.1 200 OK"
644
+ 2025-12-16 19:32:06,859 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
645
+ 2025-12-16 19:32:59,492 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
646
+ 2025-12-16 19:33:10,048 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
647
+ 2025-12-16 19:34:57,275 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
648
+ 2025-12-16 19:36:20,319 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
649
+ 2025-12-16 19:39:28,134 - app - INFO - Created new session for streaming chat: eb1c52f7-f401-4418-ac9e-68bbdfd50103
650
+ 2025-12-16 19:39:28,138 - app - INFO - Stream request from 127.0.0.1: language=english, message=hi..., session_id=eb1c52f7-f401-4418-ac9e-68bbdfd50103
651
+ 2025-12-16 19:39:29,507 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='hi', language='english', session_id='eb1c52f7-f401-4418-ac9e-68bbdfd50103'
652
+ 2025-12-16 19:39:29,800 - app - INFO - Retrieved 1 history messages for session eb1c52f7-f401-4418-ac9e-68bbdfd50103
653
+ 2025-12-16 19:39:35,078 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
654
+ 2025-12-16 19:39:35,333 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
655
+ 2025-12-16 19:39:36,012 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
656
+ 2025-12-16 19:43:47,881 - app - INFO - Created new session for streaming chat: 720ea86f-6cc1-452b-9e67-d4901d23d19d
657
+ 2025-12-16 19:43:47,886 - app - INFO - Stream request from 127.0.0.1: language=english, message=what is launch lab..., session_id=720ea86f-6cc1-452b-9e67-d4901d23d19d
658
+ 2025-12-16 19:43:50,529 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='what is launch lab', language='english', session_id='720ea86f-6cc1-452b-9e67-d4901d23d19d'
659
+ 2025-12-16 19:43:51,008 - app - INFO - Retrieved 1 history messages for session 720ea86f-6cc1-452b-9e67-d4901d23d19d
660
+ 2025-12-16 19:43:52,815 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
661
+ 2025-12-16 19:43:53,051 - tools.document_reader_tool - INFO - TOOL CALL: read_document_data called with query='about Launchlabs', source='auto'
662
+ 2025-12-16 19:43:53,203 - tools.document_reader_tool - INFO - TOOL RESULT: read_document_data found 1 result(s)
663
+ 2025-12-16 19:43:54,172 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
664
+ 2025-12-16 19:43:54,987 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
665
+ 2025-12-16 19:43:56,403 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
666
+ 2025-12-16 20:06:02,362 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
667
+ 2025-12-16 20:06:28,565 - app - INFO - Created new session for streaming chat: 5a6d92e3-f597-4f23-852b-a82f245847ac
668
+ 2025-12-16 20:06:28,566 - app - INFO - Stream request from 127.0.0.1: language=english, message=Hi..., session_id=5a6d92e3-f597-4f23-852b-a82f245847ac
669
+ 2025-12-16 20:06:29,994 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='Hi', language='english', session_id='5a6d92e3-f597-4f23-852b-a82f245847ac'
670
+ 2025-12-16 20:06:30,372 - app - INFO - Retrieved 1 history messages for session 5a6d92e3-f597-4f23-852b-a82f245847ac
671
+ 2025-12-16 20:06:34,031 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
672
+ 2025-12-16 20:06:34,745 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
673
+ 2025-12-16 20:06:35,981 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
674
+ 2025-12-16 20:06:58,192 - app - INFO - Created new session for streaming chat: c5a27fbd-3d75-4967-b002-7cf96d72b1de
675
+ 2025-12-16 20:06:58,193 - app - INFO - Stream request from 127.0.0.1: language=english, message=hi..., session_id=c5a27fbd-3d75-4967-b002-7cf96d72b1de
676
+ 2025-12-16 20:06:59,571 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='hi', language='english', session_id='c5a27fbd-3d75-4967-b002-7cf96d72b1de'
677
+ 2025-12-16 20:07:00,903 - app - INFO - Retrieved 1 history messages for session c5a27fbd-3d75-4967-b002-7cf96d72b1de
678
+ 2025-12-16 20:07:01,976 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
679
+ 2025-12-16 20:07:02,299 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
680
+ 2025-12-16 20:07:02,306 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
681
+ 2025-12-16 20:07:21,396 - app - INFO - Created new session for streaming chat: 073c034d-fbdc-480f-af33-ae3b5b9e525d
682
+ 2025-12-16 20:07:21,397 - app - INFO - Stream request from 127.0.0.1: language=english, message=Hi...., session_id=073c034d-fbdc-480f-af33-ae3b5b9e525d
683
+ 2025-12-16 20:07:23,277 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='Hi.', language='english', session_id='073c034d-fbdc-480f-af33-ae3b5b9e525d'
684
+ 2025-12-16 20:07:24,457 - app - INFO - Retrieved 1 history messages for session 073c034d-fbdc-480f-af33-ae3b5b9e525d
685
+ 2025-12-16 20:07:28,950 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
686
+ 2025-12-16 20:07:35,455 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
687
+ 2025-12-16 20:07:36,159 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
688
+ 2025-12-16 20:09:07,189 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
689
+ 2025-12-16 20:12:16,880 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
690
+ 2025-12-16 20:14:21,449 - app - INFO - Created new session for streaming chat: 9178d546-c2cf-4b12-8005-8ac91d6f69f9
691
+ 2025-12-16 20:14:21,449 - app - INFO - Stream request from 127.0.0.1: language=english, message=Hi..., session_id=9178d546-c2cf-4b12-8005-8ac91d6f69f9
692
+ 2025-12-16 20:14:22,861 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='Hi', language='english', session_id='9178d546-c2cf-4b12-8005-8ac91d6f69f9'
693
+ 2025-12-16 20:14:23,270 - app - INFO - Retrieved 1 history messages for session 9178d546-c2cf-4b12-8005-8ac91d6f69f9
694
+ 2025-12-16 20:14:26,478 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
695
+ 2025-12-16 20:14:27,273 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
696
+ 2025-12-16 20:14:27,383 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
697
+ 2025-12-16 20:17:23,859 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
698
+ 2025-12-16 20:17:31,185 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
699
+ 2025-12-16 20:17:58,834 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
700
+ 2025-12-16 20:18:06,831 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
701
+ 2025-12-16 20:18:13,599 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
702
+ 2025-12-16 20:18:25,126 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
703
+ 2025-12-16 20:18:33,527 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
704
+ 2025-12-16 20:18:39,548 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
705
+ 2025-12-16 20:19:14,129 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
706
+ 2025-12-16 20:19:24,958 - app - INFO - Created new session for streaming chat: bce1fd62-841a-4e1b-b4cc-8057c5c4b823
707
+ 2025-12-16 20:19:24,958 - app - INFO - Stream request from 127.0.0.1: language=english, message=Hi..., session_id=bce1fd62-841a-4e1b-b4cc-8057c5c4b823
708
+ 2025-12-16 20:19:26,387 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='Hi', language='english', session_id='bce1fd62-841a-4e1b-b4cc-8057c5c4b823'
709
+ 2025-12-16 20:19:26,792 - app - INFO - Retrieved 1 history messages for session bce1fd62-841a-4e1b-b4cc-8057c5c4b823
710
+ 2025-12-16 20:19:29,844 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
711
+ 2025-12-16 20:19:30,163 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
712
+ 2025-12-16 20:19:30,832 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
713
+ 2025-12-16 20:24:33,161 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
714
+ 2025-12-16 20:24:36,751 - app - INFO - Created new session for streaming chat: 707b7f47-cabd-4a3a-b2be-31577c6cce92
715
+ 2025-12-16 20:24:36,751 - app - INFO - Stream request from 127.0.0.1: language=english, message=Hi..., session_id=707b7f47-cabd-4a3a-b2be-31577c6cce92
716
+ 2025-12-16 20:24:38,182 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='Hi', language='english', session_id='707b7f47-cabd-4a3a-b2be-31577c6cce92'
717
+ 2025-12-16 20:24:38,555 - app - INFO - Retrieved 1 history messages for session 707b7f47-cabd-4a3a-b2be-31577c6cce92
718
+ 2025-12-16 20:24:41,965 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
719
+ 2025-12-16 20:24:42,792 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
720
+ 2025-12-16 20:24:43,623 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
721
+ 2025-12-16 21:05:24,848 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
722
+ 2025-12-16 21:05:39,423 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
723
+ 2025-12-16 21:28:58,753 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
724
+ 2025-12-16 21:29:29,202 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
725
+ 2025-12-16 21:29:37,560 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
726
+ 2025-12-16 21:29:55,534 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
727
+ 2025-12-16 21:30:33,665 - app - INFO - Created new session for streaming chat: 9f31df73-1217-479f-99a8-022745b9416c
728
+ 2025-12-16 21:30:33,665 - app - INFO - Stream request from 127.0.0.1: language=english, message=HI..., session_id=9f31df73-1217-479f-99a8-022745b9416c
729
+ 2025-12-16 21:30:35,034 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='HI', language='english', session_id='9f31df73-1217-479f-99a8-022745b9416c'
730
+ 2025-12-16 21:30:35,349 - app - INFO - Retrieved 1 history messages for session 9f31df73-1217-479f-99a8-022745b9416c
731
+ 2025-12-16 21:30:40,860 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
732
+ 2025-12-16 21:30:40,860 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
733
+ 2025-12-16 21:30:43,005 - app - INFO - Added assistant response to session history: 9f31df73-1217-479f-99a8-022745b9416c
734
+ 2025-12-16 21:30:43,005 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
735
+ 2025-12-16 21:31:35,335 - app - INFO - Created new session for streaming chat: 729fe42b-365c-4b59-8b12-b1e37026a685
736
+ 2025-12-16 21:31:35,361 - app - INFO - Stream request from 127.0.0.1: language=english, message=hi..., session_id=729fe42b-365c-4b59-8b12-b1e37026a685
737
+ 2025-12-16 21:31:36,908 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='hi', language='english', session_id='729fe42b-365c-4b59-8b12-b1e37026a685'
738
+ 2025-12-16 21:31:37,934 - app - INFO - Retrieved 1 history messages for session 729fe42b-365c-4b59-8b12-b1e37026a685
739
+ 2025-12-16 21:31:39,386 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
740
+ 2025-12-16 21:31:39,653 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
741
+ 2025-12-16 21:31:41,252 - app - INFO - Added assistant response to session history: 729fe42b-365c-4b59-8b12-b1e37026a685
742
+ 2025-12-16 21:31:41,252 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
743
+ 2025-12-16 21:32:01,046 - app - INFO - Created new session for streaming chat: d58af680-dbc3-4181-8722-94f8850efff1
744
+ 2025-12-16 21:32:01,046 - app - INFO - Stream request from 127.0.0.1: language=english, message=launchLab..., session_id=d58af680-dbc3-4181-8722-94f8850efff1
745
+ 2025-12-16 21:32:02,418 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='launchLab', language='english', session_id='d58af680-dbc3-4181-8722-94f8850efff1'
746
+ 2025-12-16 21:32:03,576 - app - INFO - Retrieved 1 history messages for session d58af680-dbc3-4181-8722-94f8850efff1
747
+ 2025-12-16 21:32:04,726 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
748
+ 2025-12-16 21:32:05,191 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
749
+ 2025-12-16 21:32:07,485 - app - INFO - Added assistant response to session history: d58af680-dbc3-4181-8722-94f8850efff1
750
+ 2025-12-16 21:32:07,485 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
751
+ 2025-12-16 21:42:58,223 - app - INFO - Created new session for streaming chat: 8db465b1-b15a-4a91-af51-e503775f8bda
752
+ 2025-12-16 21:42:58,232 - app - INFO - Stream request from 127.0.0.1: language=english, message=Hi..., session_id=8db465b1-b15a-4a91-af51-e503775f8bda
753
+ 2025-12-16 21:42:59,606 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='Hi', language='english', session_id='8db465b1-b15a-4a91-af51-e503775f8bda'
754
+ 2025-12-16 21:42:59,907 - app - INFO - Retrieved 1 history messages for session 8db465b1-b15a-4a91-af51-e503775f8bda
755
+ 2025-12-16 21:43:01,407 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
756
+ 2025-12-16 21:43:01,766 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
757
+ 2025-12-16 21:43:03,532 - app - INFO - Added assistant response to session history: 8db465b1-b15a-4a91-af51-e503775f8bda
758
+ 2025-12-16 21:43:03,532 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
759
+ 2025-12-16 21:43:37,898 - app - INFO - Created new session for streaming chat: cd97c919-aa19-444d-8bcc-39a9b9cbc219
760
+ 2025-12-16 21:43:37,898 - app - INFO - Stream request from 127.0.0.1: language=english, message=what is LaunchLab?..., session_id=cd97c919-aa19-444d-8bcc-39a9b9cbc219
761
+ 2025-12-16 21:43:39,500 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='what is LaunchLab?', language='english', session_id='cd97c919-aa19-444d-8bcc-39a9b9cbc219'
762
+ 2025-12-16 21:43:40,334 - app - INFO - Retrieved 1 history messages for session cd97c919-aa19-444d-8bcc-39a9b9cbc219
763
+ 2025-12-16 21:43:42,880 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
764
+ 2025-12-16 21:43:43,297 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
765
+ 2025-12-16 21:43:45,438 - app - INFO - Added assistant response to session history: cd97c919-aa19-444d-8bcc-39a9b9cbc219
766
+ 2025-12-16 21:43:45,446 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
767
+ 2025-12-16 22:02:38,297 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
768
+ 2025-12-16 22:02:44,677 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
769
+ 2025-12-16 22:02:59,394 - app - INFO - Created new session for streaming chat: e4a3bf24-b781-4535-9cc7-5a6fb2721741
770
+ 2025-12-16 22:02:59,395 - app - INFO - Stream request from 127.0.0.1: language=english, message=Hi..., session_id=e4a3bf24-b781-4535-9cc7-5a6fb2721741
771
+ 2025-12-16 22:03:00,863 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='Hi', language='english', session_id='e4a3bf24-b781-4535-9cc7-5a6fb2721741'
772
+ 2025-12-16 22:03:01,232 - app - INFO - Retrieved 1 history messages for session e4a3bf24-b781-4535-9cc7-5a6fb2721741
773
+ 2025-12-16 22:03:05,060 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
774
+ 2025-12-16 22:03:06,392 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
775
+ 2025-12-16 22:03:08,375 - app - INFO - Added assistant response to session history: e4a3bf24-b781-4535-9cc7-5a6fb2721741
776
+ 2025-12-16 22:03:08,376 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
777
+ 2025-12-16 22:17:07,592 - app - INFO - Created new session for streaming chat: 199cf206-0d20-45ec-bb6d-5f4a5115ed7e
778
+ 2025-12-16 22:17:07,612 - app - INFO - Stream request from 127.0.0.1: language=english, message=Hi..., session_id=199cf206-0d20-45ec-bb6d-5f4a5115ed7e
779
+ 2025-12-16 22:17:09,000 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='Hi', language='english', session_id='199cf206-0d20-45ec-bb6d-5f4a5115ed7e'
780
+ 2025-12-16 22:17:09,296 - app - INFO - Retrieved 1 history messages for session 199cf206-0d20-45ec-bb6d-5f4a5115ed7e
781
+ 2025-12-16 22:17:11,267 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
782
+ 2025-12-16 22:17:11,294 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
783
+ 2025-12-16 22:17:13,018 - app - INFO - Added assistant response to session history: 199cf206-0d20-45ec-bb6d-5f4a5115ed7e
784
+ 2025-12-16 22:17:13,019 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
785
+ 2025-12-16 22:18:01,709 - app - INFO - Created new session for streaming chat: 9969da60-b7e9-460f-99a5-c06b0431421b
786
+ 2025-12-16 22:18:01,710 - app - INFO - Stream request from 127.0.0.1: language=english, message=what is launchlab..., session_id=9969da60-b7e9-460f-99a5-c06b0431421b
787
+ 2025-12-16 22:18:03,373 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='what is launchlab', language='english', session_id='9969da60-b7e9-460f-99a5-c06b0431421b'
788
+ 2025-12-16 22:18:04,395 - app - INFO - Retrieved 1 history messages for session 9969da60-b7e9-460f-99a5-c06b0431421b
789
+ 2025-12-16 22:18:05,834 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
790
+ 2025-12-16 22:18:07,279 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
791
+ 2025-12-16 22:18:08,699 - app - INFO - Added assistant response to session history: 9969da60-b7e9-460f-99a5-c06b0431421b
792
+ 2025-12-16 22:18:08,701 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
793
+ 2025-12-16 22:26:40,733 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
794
+ 2025-12-16 22:27:10,754 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
795
+ 2025-12-16 22:27:19,432 - app - INFO - CORS enabled for origins: ['http://localhost:3001', 'http://localhost:8000', 'http://0.0.0.0:7860', 'http://localhost:7860']
796
+ 2025-12-16 22:28:18,303 - app - INFO - Created new session for streaming chat: ce7c054c-a42f-4d7c-a953-d0cc8e4ecb76
797
+ 2025-12-16 22:28:18,474 - app - INFO - Stream request from 127.0.0.1: language=english, message=Hi..., session_id=ce7c054c-a42f-4d7c-a953-d0cc8e4ecb76
798
+ 2025-12-16 22:28:19,886 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='Hi', language='english', session_id='ce7c054c-a42f-4d7c-a953-d0cc8e4ecb76'
799
+ 2025-12-16 22:28:20,251 - app - INFO - Retrieved 1 history messages for session ce7c054c-a42f-4d7c-a953-d0cc8e4ecb76
800
+ 2025-12-16 22:28:23,732 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
801
+ 2025-12-16 22:28:23,862 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
802
+ 2025-12-16 22:28:25,762 - app - INFO - Added assistant response to session history: ce7c054c-a42f-4d7c-a953-d0cc8e4ecb76
803
+ 2025-12-16 22:28:25,763 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
804
+ 2025-12-16 22:28:57,673 - app - INFO - Created new session for streaming chat: 01eea8e8-0fe3-4950-8d91-24d9aa3f58dd
805
+ 2025-12-16 22:28:57,673 - app - INFO - Stream request from 127.0.0.1: language=english, message=what is launchlab..., session_id=01eea8e8-0fe3-4950-8d91-24d9aa3f58dd
806
+ 2025-12-16 22:28:59,028 - app - INFO - AGENT STREAM CALL: query_launchlabs_bot_stream called with message='what is launchlab', language='english', session_id='01eea8e8-0fe3-4950-8d91-24d9aa3f58dd'
807
+ 2025-12-16 22:29:00,066 - app - INFO - Retrieved 1 history messages for session 01eea8e8-0fe3-4950-8d91-24d9aa3f58dd
808
+ 2025-12-16 22:29:01,807 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
809
+ 2025-12-16 22:29:02,422 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
810
+ 2025-12-16 22:29:04,825 - app - INFO - Added assistant response to session history: 01eea8e8-0fe3-4950-8d91-24d9aa3f58dd
811
+ 2025-12-16 22:29:04,827 - app - INFO - AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully
app.py CHANGED
@@ -1,7 +1,832 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
  FastAPI application for Launchlabs Chatbot API
3
  Provides /chat and /chat-stream endpoints with rate limiting, CORS, and error handling
4
- Updated with language context support
5
  """
6
  import os
7
  import logging
@@ -183,11 +1008,254 @@ def is_meeting_rate_limited(ip_address: str) -> bool:
183
  return False
184
 
185
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  def query_launchlabs_bot_stream(user_message: str, language: str = "english", session_id: Optional[str] = None):
187
  """
188
- Query the Launchlabs bot with streaming - returns async generator.
189
- Now includes language context and session history.
190
- Implements fallback to non-streaming when streaming fails (e.g., with Gemini models).
191
  """
192
  logger.info(f"AGENT STREAM CALL: query_launchlabs_bot_stream called with message='{user_message}', language='{language}', session_id='{session_id}'")
193
 
@@ -213,46 +1281,38 @@ def query_launchlabs_bot_stream(user_message: str, language: str = "english", se
213
 
214
  async def generate_stream():
215
  try:
216
- previous = ""
217
- has_streamed = True
218
 
219
  try:
220
- # Attempt streaming with error handling for each event
221
  async for event in result.stream_events():
222
  try:
223
  if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
224
- delta = event.data.delta or ""
225
-
226
- # ---- Spacing Fix ----
227
- if (
228
- previous
229
- and not previous.endswith((" ", "\n"))
230
- and not delta.startswith((" ", ".", ",", "?", "!", ":", ";"))
231
- ):
232
- delta = " " + delta
233
-
234
- previous = delta
235
- # ---- End Fix ----
236
-
237
- yield f"data: {delta}\n\n"
238
- has_streamed = True
239
  except Exception as event_error:
240
- # Handle individual event errors (e.g., missing logprobs field)
241
  logger.warning(f"Event processing error: {event_error}")
242
  continue
243
 
 
 
 
 
 
244
  yield "data: [DONE]\n\n"
245
  logger.info("AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully")
246
 
247
  except Exception as stream_error:
248
- # Fallback to non-streaming if streaming fails
249
  logger.warning(f"Streaming failed, falling back to non-streaming: {stream_error}")
250
 
251
  if not has_streamed:
252
- # Get final output using the streaming result's final_output property
253
- # Wait for the stream to complete to get final output
254
  try:
255
- # Use the non-streaming API as fallback
256
  fallback_response = await Runner.run(
257
  launchlabs_assistant,
258
  input=user_message,
@@ -271,6 +1331,10 @@ def query_launchlabs_bot_stream(user_message: str, language: str = "english", se
271
  else:
272
  response_text = str(final_output)
273
 
 
 
 
 
274
  yield f"data: {response_text}\n\n"
275
  yield "data: [DONE]\n\n"
276
  logger.info("AGENT STREAM RESULT: query_launchlabs_bot_stream fallback completed successfully")
@@ -278,7 +1342,6 @@ def query_launchlabs_bot_stream(user_message: str, language: str = "english", se
278
  logger.error(f"Fallback also failed: {fallback_error}", exc_info=True)
279
  yield f"data: [ERROR] Unable to complete request.\n\n"
280
  else:
281
- # Already streamed some content, just end gracefully
282
  yield "data: [DONE]\n\n"
283
 
284
  except InputGuardrailTripwireTriggered as e:
@@ -300,6 +1363,14 @@ def query_launchlabs_bot_stream(user_message: str, language: str = "english", se
300
  return error_stream()
301
 
302
 
 
 
 
 
 
 
 
 
303
  async def query_launchlabs_bot(user_message: str, language: str = "english", session_id: Optional[str] = None):
304
  """
305
  Query the Launchlabs bot - returns complete response.
@@ -487,6 +1558,7 @@ async def api_messages(request: Request, chat_request: ChatRequest):
487
  detail="Internal error – try again."
488
  )
489
 
 
490
  @app.post("/chat-stream")
491
  @limiter.limit("10/minute") # Limit to 10 requests per minute per IP
492
  async def chat_stream(request: Request, chat_request: ChatRequest):
@@ -516,9 +1588,7 @@ async def chat_stream(request: Request, chat_request: ChatRequest):
516
  session_id=session_id
517
  )
518
 
519
- # Note: For streaming, we add the response to history after the stream completes
520
- # This would need to be handled in the frontend by making a separate call or
521
- # by modifying the stream generator to add the complete response to history
522
 
523
  return StreamingResponse(
524
  stream_generator,
 
1
+ # """
2
+ # FastAPI application for Launchlabs Chatbot API
3
+ # Provides /chat and /chat-stream endpoints with rate limiting, CORS, and error handling
4
+ # Updated with language context support
5
+ # """
6
+ # import os
7
+ # import logging
8
+ # import time
9
+ # from typing import Optional
10
+ # from collections import defaultdict
11
+ # import resend
12
+
13
+ # from fastapi import FastAPI, Request, HTTPException, status, Depends, Header
14
+ # from fastapi.responses import StreamingResponse, JSONResponse
15
+ # from fastapi.middleware.cors import CORSMiddleware
16
+ # from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
17
+ # from pydantic import BaseModel
18
+ # from slowapi import Limiter, _rate_limit_exceeded_handler
19
+ # from slowapi.util import get_remote_address
20
+ # from slowapi.errors import RateLimitExceeded
21
+ # from slowapi.middleware import SlowAPIMiddleware
22
+ # from dotenv import load_dotenv
23
+
24
+ # from agents import Runner, RunContextWrapper
25
+ # from agents.exceptions import InputGuardrailTripwireTriggered
26
+ # from openai.types.responses import ResponseTextDeltaEvent
27
+ # from chatbot.chatbot_agent import launchlabs_assistant
28
+ # from sessions.session_manager import session_manager
29
+
30
+ # # Load environment variables
31
+ # load_dotenv()
32
+
33
+ # # Configure Resend
34
+ # resend.api_key = os.getenv("RESEND_API_KEY")
35
+
36
+ # # Configure logging
37
+ # logging.basicConfig(
38
+ # level=logging.INFO,
39
+ # format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
40
+ # handlers=[
41
+ # logging.FileHandler('app.log'),
42
+ # logging.StreamHandler()
43
+ # ]
44
+ # )
45
+ # logger = logging.getLogger(__name__)
46
+
47
+ # # Initialize rate limiter with enhanced security
48
+ # limiter = Limiter(key_func=get_remote_address, default_limits=["100/day", "20/hour", "3/minute"])
49
+
50
+ # # Create FastAPI app
51
+ # app = FastAPI(
52
+ # title="Launchlabs Chatbot API",
53
+ # description="AI-powered chatbot API for Launchlabs services",
54
+ # version="1.0.0"
55
+ # )
56
+
57
+ # # Add rate limiter middleware
58
+ # app.state.limiter = limiter
59
+ # app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
60
+ # app.add_middleware(SlowAPIMiddleware)
61
+
62
+ # # Configure CORS from environment variable
63
+ # allowed_origins = os.getenv("ALLOWED_ORIGINS", "").split(",")
64
+ # allowed_origins = [origin.strip() for origin in allowed_origins if origin.strip()]
65
+
66
+ # if allowed_origins:
67
+ # app.add_middleware(
68
+ # CORSMiddleware,
69
+ # allow_origins=["*"] + allowed_origins,
70
+ # allow_credentials=True,
71
+ # allow_methods=["*"],
72
+ # allow_headers=["*"],
73
+ # )
74
+ # logger.info(f"CORS enabled for origins: {allowed_origins}")
75
+ # else:
76
+ # logger.warning("No ALLOWED_ORIGINS set in .env - CORS disabled")
77
+
78
+ # # Security setup
79
+ # security = HTTPBearer()
80
+
81
+ # # Enhanced rate limiting dictionaries
82
+ # request_counts = defaultdict(list) # Track requests per IP
83
+ # TICKET_RATE_LIMIT = 5 # Max 5 tickets per hour per IP
84
+ # TICKET_TIME_WINDOW = 3600 # 1 hour in seconds
85
+ # MEETING_RATE_LIMIT = 3 # Max 3 meetings per hour per IP
86
+ # MEETING_TIME_WINDOW = 3600 # 1 hour in seconds
87
+
88
+ # # Request/Response models
89
+ # class ChatRequest(BaseModel):
90
+ # message: str
91
+ # language: Optional[str] = "english" # Default to English if not specified
92
+ # session_id: Optional[str] = None # Session ID for chat history
93
+
94
+
95
+ # class ChatResponse(BaseModel):
96
+ # response: str
97
+ # success: bool
98
+ # session_id: str # Include session ID in response
99
+
100
+
101
+ # class ErrorResponse(BaseModel):
102
+ # error: str
103
+ # detail: Optional[str] = None
104
+
105
+
106
+ # class TicketRequest(BaseModel):
107
+ # name: str
108
+ # email: str
109
+ # message: str
110
+
111
+
112
+ # class TicketResponse(BaseModel):
113
+ # success: bool
114
+ # message: str
115
+
116
+
117
+ # class MeetingRequest(BaseModel):
118
+ # name: str
119
+ # email: str
120
+ # date: str # ISO format date string
121
+ # time: str # Time in HH:MM format
122
+ # timezone: str # Timezone identifier
123
+ # duration: int # Duration in minutes
124
+ # topic: str # Meeting topic/title
125
+ # attendees: list[str] # List of attendee emails
126
+ # description: Optional[str] = None # Optional meeting description
127
+ # location: Optional[str] = "Google Meet" # Meeting location/platform
128
+
129
+
130
+ # class MeetingResponse(BaseModel):
131
+ # success: bool
132
+ # message: str
133
+ # meeting_id: Optional[str] = None # Unique identifier for the meeting
134
+
135
+
136
+ # # Security dependency for API key validation
137
+ # async def verify_api_key(credentials: HTTPAuthorizationCredentials = Depends(security)):
138
+ # """Verify API key for protected endpoints"""
139
+ # # In production, you would check against a database of valid keys
140
+ # # For now, we'll use an environment variable
141
+ # expected_key = os.getenv("API_KEY")
142
+ # if not expected_key or credentials.credentials != expected_key:
143
+ # raise HTTPException(
144
+ # status_code=status.HTTP_401_UNAUTHORIZED,
145
+ # detail="Invalid or missing API key",
146
+ # )
147
+ # return credentials.credentials
148
+
149
+
150
+ # def is_ticket_rate_limited(ip_address: str) -> bool:
151
+ # """Check if an IP address has exceeded ticket submission rate limits"""
152
+ # current_time = time.time()
153
+ # # Clean old requests outside the time window
154
+ # request_counts[ip_address] = [
155
+ # req_time for req_time in request_counts[ip_address]
156
+ # if current_time - req_time < TICKET_TIME_WINDOW
157
+ # ]
158
+
159
+ # # Check if limit exceeded
160
+ # if len(request_counts[ip_address]) >= TICKET_RATE_LIMIT:
161
+ # return True
162
+
163
+ # # Add current request
164
+ # request_counts[ip_address].append(current_time)
165
+ # return False
166
+
167
+
168
+ # def is_meeting_rate_limited(ip_address: str) -> bool:
169
+ # """Check if an IP address has exceeded meeting scheduling rate limits"""
170
+ # current_time = time.time()
171
+ # # Clean old requests outside the time window
172
+ # request_counts[ip_address] = [
173
+ # req_time for req_time in request_counts[ip_address]
174
+ # if current_time - req_time < MEETING_TIME_WINDOW
175
+ # ]
176
+
177
+ # # Check if limit exceeded
178
+ # if len(request_counts[ip_address]) >= MEETING_RATE_LIMIT:
179
+ # return True
180
+
181
+ # # Add current request
182
+ # request_counts[ip_address].append(current_time)
183
+ # return False
184
+
185
+
186
+ # def query_launchlabs_bot_stream(user_message: str, language: str = "english", session_id: Optional[str] = None):
187
+ # """
188
+ # Query the Launchlabs bot with streaming - returns async generator.
189
+ # Now includes language context and session history.
190
+ # Implements fallback to non-streaming when streaming fails (e.g., with Gemini models).
191
+ # """
192
+ # logger.info(f"AGENT STREAM CALL: query_launchlabs_bot_stream called with message='{user_message}', language='{language}', session_id='{session_id}'")
193
+
194
+ # # Get session history if session_id is provided
195
+ # history = []
196
+ # if session_id:
197
+ # history = session_manager.get_session_history(session_id)
198
+ # logger.info(f"Retrieved {len(history)} history messages for session {session_id}")
199
+
200
+ # try:
201
+ # # Create context with language preference and history
202
+ # context_data = {"language": language}
203
+ # if history:
204
+ # context_data["history"] = history
205
+
206
+ # ctx = RunContextWrapper(context=context_data)
207
+
208
+ # result = Runner.run_streamed(
209
+ # launchlabs_assistant,
210
+ # input=user_message,
211
+ # context=ctx.context
212
+ # )
213
+
214
+ # async def generate_stream():
215
+ # try:
216
+ # previous = ""
217
+ # has_streamed = True
218
+
219
+ # try:
220
+ # # Attempt streaming with error handling for each event
221
+ # async for event in result.stream_events():
222
+ # try:
223
+ # if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
224
+ # delta = event.data.delta or ""
225
+
226
+ # # ---- Spacing Fix ----
227
+ # if (
228
+ # previous
229
+ # and not previous.endswith((" ", "\n"))
230
+ # and not delta.startswith((" ", ".", ",", "?", "!", ":", ";"))
231
+ # ):
232
+ # delta = " " + delta
233
+
234
+ # previous = delta
235
+ # # ---- End Fix ----
236
+
237
+ # yield f"data: {delta}\n\n"
238
+ # has_streamed = True
239
+ # except Exception as event_error:
240
+ # # Handle individual event errors (e.g., missing logprobs field)
241
+ # logger.warning(f"Event processing error: {event_error}")
242
+ # continue
243
+
244
+ # yield "data: [DONE]\n\n"
245
+ # logger.info("AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully")
246
+
247
+ # except Exception as stream_error:
248
+ # # Fallback to non-streaming if streaming fails
249
+ # logger.warning(f"Streaming failed, falling back to non-streaming: {stream_error}")
250
+
251
+ # if not has_streamed:
252
+ # # Get final output using the streaming result's final_output property
253
+ # # Wait for the stream to complete to get final output
254
+ # try:
255
+ # # Use the non-streaming API as fallback
256
+ # fallback_response = await Runner.run(
257
+ # launchlabs_assistant,
258
+ # input=user_message,
259
+ # context=ctx.context
260
+ # )
261
+
262
+ # if hasattr(fallback_response, 'final_output'):
263
+ # final_output = fallback_response.final_output
264
+ # else:
265
+ # final_output = fallback_response
266
+
267
+ # if hasattr(final_output, 'content'):
268
+ # response_text = final_output.content
269
+ # elif isinstance(final_output, str):
270
+ # response_text = final_output
271
+ # else:
272
+ # response_text = str(final_output)
273
+
274
+ # yield f"data: {response_text}\n\n"
275
+ # yield "data: [DONE]\n\n"
276
+ # logger.info("AGENT STREAM RESULT: query_launchlabs_bot_stream fallback completed successfully")
277
+ # except Exception as fallback_error:
278
+ # logger.error(f"Fallback also failed: {fallback_error}", exc_info=True)
279
+ # yield f"data: [ERROR] Unable to complete request.\n\n"
280
+ # else:
281
+ # # Already streamed some content, just end gracefully
282
+ # yield "data: [DONE]\n\n"
283
+
284
+ # except InputGuardrailTripwireTriggered as e:
285
+ # logger.warning(f"Guardrail blocked query during streaming: {e}")
286
+ # yield f"data: [ERROR] Query was blocked by content guardrail.\n\n"
287
+
288
+ # except Exception as e:
289
+ # logger.error(f"Streaming error: {e}", exc_info=True)
290
+ # yield f"data: [ERROR] {str(e)}\n\n"
291
+
292
+ # return generate_stream()
293
+
294
+ # except Exception as e:
295
+ # logger.error(f"Error setting up stream: {e}", exc_info=True)
296
+
297
+ # async def error_stream():
298
+ # yield f"data: [ERROR] Failed to initialize stream.\n\n"
299
+
300
+ # return error_stream()
301
+
302
+
303
+ # async def query_launchlabs_bot(user_message: str, language: str = "english", session_id: Optional[str] = None):
304
+ # """
305
+ # Query the Launchlabs bot - returns complete response.
306
+ # Now includes language context and session history.
307
+ # """
308
+ # logger.info(f"AGENT CALL: query_launchlabs_bot called with message='{user_message}', language='{language}', session_id='{session_id}'")
309
+
310
+ # # Get session history if session_id is provided
311
+ # history = []
312
+ # if session_id:
313
+ # history = session_manager.get_session_history(session_id)
314
+ # logger.info(f"Retrieved {len(history)} history messages for session {session_id}")
315
+
316
+ # try:
317
+ # # Create context with language preference and history
318
+ # context_data = {"language": language}
319
+ # if history:
320
+ # context_data["history"] = history
321
+
322
+ # ctx = RunContextWrapper(context=context_data)
323
+
324
+ # response = await Runner.run(
325
+ # launchlabs_assistant,
326
+ # input=user_message,
327
+ # context=ctx.context
328
+ # )
329
+ # logger.info("AGENT RESULT: query_launchlabs_bot completed successfully")
330
+ # return response.final_output
331
+
332
+ # except InputGuardrailTripwireTriggered as e:
333
+ # logger.warning(f"Guardrail blocked query: {e}")
334
+ # raise HTTPException(
335
+ # status_code=status.HTTP_403_FORBIDDEN,
336
+ # detail="Query was blocked by content guardrail. Please ensure your query is related to Launchlabs services."
337
+ # )
338
+ # except Exception as e:
339
+ # logger.error(f"Error in query_launchlabs_bot: {e}", exc_info=True)
340
+ # raise HTTPException(
341
+ # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
342
+ # detail="An internal error occurred while processing your request."
343
+ # )
344
+
345
+
346
+ # @app.get("/")
347
+ # async def root():
348
+ # return {"status": "ok", "service": "Launchlabs Chatbot API"}
349
+
350
+
351
+ # @app.get("/health")
352
+ # async def health():
353
+ # return {"status": "healthy"}
354
+
355
+
356
+ # @app.post("/session")
357
+ # async def create_session():
358
+ # """
359
+ # Create a new chat session
360
+ # Returns a session ID that can be used to maintain chat history
361
+ # """
362
+ # try:
363
+ # session_id = session_manager.create_session()
364
+ # logger.info(f"Created new session: {session_id}")
365
+ # return {"session_id": session_id, "message": "Session created successfully"}
366
+ # except Exception as e:
367
+ # logger.error(f"Error creating session: {e}", exc_info=True)
368
+ # raise HTTPException(
369
+ # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
370
+ # detail="Failed to create session"
371
+ # )
372
+
373
+
374
+ # @app.post("/chat", response_model=ChatResponse)
375
+ # @limiter.limit("10/minute") # Limit to 10 requests per minute per IP
376
+ # async def chat(request: Request, chat_request: ChatRequest):
377
+ # """
378
+ # Standard chat endpoint with language support and session history.
379
+ # Accepts: {"message": "...", "language": "norwegian", "session_id": "optional-session-id"}
380
+ # """
381
+ # try:
382
+ # # Create or use existing session
383
+ # session_id = chat_request.session_id
384
+ # if not session_id:
385
+ # session_id = session_manager.create_session()
386
+ # logger.info(f"Created new session for chat: {session_id}")
387
+
388
+ # logger.info(
389
+ # f"Chat request from {get_remote_address(request)}: "
390
+ # f"language={chat_request.language}, message={chat_request.message[:50]}..., session_id={session_id}"
391
+ # )
392
+
393
+ # # Add user message to session history
394
+ # session_manager.add_message_to_history(session_id, "user", chat_request.message)
395
+
396
+ # # Pass language and session to the bot
397
+ # response = await query_launchlabs_bot(
398
+ # chat_request.message,
399
+ # language=chat_request.language,
400
+ # session_id=session_id
401
+ # )
402
+
403
+ # if hasattr(response, 'content'):
404
+ # response_text = response.content
405
+ # elif isinstance(response, str):
406
+ # response_text = response
407
+ # else:
408
+ # response_text = str(response)
409
+
410
+ # # Add bot response to session history
411
+ # session_manager.add_message_to_history(session_id, "assistant", response_text)
412
+
413
+ # logger.info(f"Chat response generated successfully in {chat_request.language} for session {session_id}")
414
+
415
+ # return ChatResponse(
416
+ # response=response_text,
417
+ # success=True,
418
+ # session_id=session_id
419
+ # )
420
+
421
+ # except HTTPException:
422
+ # raise
423
+ # except Exception as e:
424
+ # logger.error(f"Unexpected error in /chat: {e}", exc_info=True)
425
+ # raise HTTPException(
426
+ # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
427
+ # detail="An internal error occurred while processing your request."
428
+ # )
429
+
430
+
431
+ # @app.post("/api/messages", response_model=ChatResponse)
432
+ # @limiter.limit("10/minute") # Same rate limit as /chat
433
+ # async def api_messages(request: Request, chat_request: ChatRequest):
434
+ # """
435
+ # Frontend-friendly chat endpoint at /api/messages.
436
+ # Exactly mirrors /chat logic for session/history support.
437
+ # Expects: {"message": "...", "language": "english", "session_id": "optional"}
438
+ # """
439
+ # client_ip = get_remote_address(request)
440
+ # logger.info(f"API Messages request from {client_ip}: message='{chat_request.message[:50]}...', lang='{chat_request.language}', session='{chat_request.session_id}'")
441
+
442
+ # try:
443
+ # # Create/use session (Firestore-backed)
444
+ # session_id = chat_request.session_id
445
+ # if not session_id:
446
+ # session_id = session_manager.create_session()
447
+ # logger.info(f"New session created for /api/messages: {session_id}")
448
+
449
+ # # Save user message to history
450
+ # session_manager.add_message_to_history(session_id, "user", chat_request.message)
451
+
452
+ # # Call your existing bot query function
453
+ # response = await query_launchlabs_bot(
454
+ # user_message=chat_request.message,
455
+ # language=chat_request.language,
456
+ # session_id=session_id
457
+ # )
458
+
459
+ # # Extract response text
460
+ # response_text = (
461
+ # response.content if hasattr(response, 'content')
462
+ # else response if isinstance(response, str)
463
+ # else str(response)
464
+ # )
465
+
466
+ # # Save AI response to history
467
+ # session_manager.add_message_to_history(session_id, "assistant", response_text)
468
+
469
+ # logger.info(f"API Messages success: Response sent for session {session_id}")
470
+
471
+ # return ChatResponse(
472
+ # response=response_text,
473
+ # success=True,
474
+ # session_id=session_id
475
+ # )
476
+
477
+ # except InputGuardrailTripwireTriggered as e:
478
+ # logger.warning(f"Guardrail blocked /api/messages: {e}")
479
+ # raise HTTPException(
480
+ # status_code=403,
481
+ # detail="Query blocked – please ask about Launchlabs services."
482
+ # )
483
+ # except Exception as e:
484
+ # logger.error(f"Error in /api/messages: {e}", exc_info=True)
485
+ # raise HTTPException(
486
+ # status_code=500,
487
+ # detail="Internal error – try again."
488
+ # )
489
+
490
+ # @app.post("/chat-stream")
491
+ # @limiter.limit("10/minute") # Limit to 10 requests per minute per IP
492
+ # async def chat_stream(request: Request, chat_request: ChatRequest):
493
+ # """
494
+ # Streaming chat endpoint with language support and session history.
495
+ # Accepts: {"message": "...", "language": "norwegian", "session_id": "optional-session-id"}
496
+ # """
497
+ # try:
498
+ # # Create or use existing session
499
+ # session_id = chat_request.session_id
500
+ # if not session_id:
501
+ # session_id = session_manager.create_session()
502
+ # logger.info(f"Created new session for streaming chat: {session_id}")
503
+
504
+ # logger.info(
505
+ # f"Stream request from {get_remote_address(request)}: "
506
+ # f"language={chat_request.language}, message={chat_request.message[:50]}..., session_id={session_id}"
507
+ # )
508
+
509
+ # # Add user message to session history
510
+ # session_manager.add_message_to_history(session_id, "user", chat_request.message)
511
+
512
+ # # Pass language and session to the streaming bot
513
+ # stream_generator = query_launchlabs_bot_stream(
514
+ # chat_request.message,
515
+ # language=chat_request.language,
516
+ # session_id=session_id
517
+ # )
518
+
519
+ # # Note: For streaming, we add the response to history after the stream completes
520
+ # # This would need to be handled in the frontend by making a separate call or
521
+ # # by modifying the stream generator to add the complete response to history
522
+
523
+ # return StreamingResponse(
524
+ # stream_generator,
525
+ # media_type="text/event-stream",
526
+ # headers={
527
+ # "Cache-Control": "no-cache",
528
+ # "Connection": "keep-alive",
529
+ # "X-Accel-Buffering": "no",
530
+ # "Session-ID": session_id # Include session ID in headers
531
+ # }
532
+ # )
533
+
534
+ # except HTTPException:
535
+ # raise
536
+ # except Exception as e:
537
+ # logger.error(f"Unexpected error in /chat-stream: {e}", exc_info=True)
538
+ # raise HTTPException(
539
+ # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
540
+ # detail="An internal error occurred while processing your request."
541
+ # )
542
+
543
+
544
+ # @app.post("/ticket", response_model=TicketResponse)
545
+ # @limiter.limit("5/hour") # Limit to 5 tickets per hour per IP
546
+ # async def submit_ticket(request: Request, ticket_request: TicketRequest):
547
+ # """
548
+ # Submit a support ticket via email using Resend API.
549
+ # Accepts: {"name": "John Doe", "email": "john@example.com", "message": "Issue description"}
550
+ # """
551
+ # try:
552
+ # client_ip = get_remote_address(request)
553
+ # logger.info(f"Ticket submission request from {ticket_request.name} ({ticket_request.email}) - IP: {client_ip}")
554
+
555
+ # # Additional rate limiting for tickets
556
+ # if is_ticket_rate_limited(client_ip):
557
+ # logger.warning(f"Rate limit exceeded for ticket submission from IP: {client_ip}")
558
+ # raise HTTPException(
559
+ # status_code=status.HTTP_429_TOO_MANY_REQUESTS,
560
+ # detail="Too many ticket submissions. Please try again later."
561
+ # )
562
+
563
+ # # Get admin email from environment variables or use a default
564
+ # admin_email = os.getenv("ADMIN_EMAIL", "admin@yourcompany.com")
565
+
566
+ # # Use a verified sender email (you need to verify this in your Resend account)
567
+ # # For testing purposes, you can use your Resend account's verified domain
568
+ # sender_email = os.getenv("SENDER_EMAIL", "onboarding@resend.dev")
569
+
570
+ # # Prepare the email using Resend
571
+ # params = {
572
+ # "from": sender_email,
573
+ # "to": [admin_email],
574
+ # "subject": f"Support Ticket from {ticket_request.name}",
575
+ # "html": f"""
576
+ # <p>Hello Admin,</p>
577
+ # <p>A new support ticket has been submitted:</p>
578
+ # <p><strong>Name:</strong> {ticket_request.name}</p>
579
+ # <p><strong>Email:</strong> {ticket_request.email}</p>
580
+ # <p><strong>Message:</strong></p>
581
+ # <p>{ticket_request.message}</p>
582
+ # <p><strong>IP Address:</strong> {client_ip}</p>
583
+ # <br>
584
+ # <p>Best regards,<br>Launchlabs Support Team</p>
585
+ # """
586
+ # }
587
+
588
+ # # Send the email
589
+ # email = resend.Emails.send(params)
590
+
591
+ # logger.info(f"Ticket submitted successfully by {ticket_request.name} from IP: {client_ip}")
592
+
593
+ # return TicketResponse(
594
+ # success=True,
595
+ # message="Ticket submitted successfully. We'll get back to you soon."
596
+ # )
597
+
598
+ # except HTTPException:
599
+ # raise
600
+ # except Exception as e:
601
+ # logger.error(f"Error submitting ticket: {e}", exc_info=True)
602
+ # raise HTTPException(
603
+ # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
604
+ # detail="Failed to submit ticket. Please try again later."
605
+ # )
606
+
607
+
608
+ # @app.post("/schedule-meeting", response_model=MeetingResponse)
609
+ # @limiter.limit("3/hour") # Limit to 3 meetings per hour per IP
610
+ # async def schedule_meeting(request: Request, meeting_request: MeetingRequest):
611
+ # """
612
+ # Schedule a meeting and send email invitations using Resend API.
613
+ # Accepts meeting details and sends professional email invitations to organizer and attendees.
614
+ # """
615
+ # try:
616
+ # client_ip = get_remote_address(request)
617
+ # logger.info(f"Meeting scheduling request from {meeting_request.name} ({meeting_request.email}) - IP: {client_ip}")
618
+
619
+ # # Additional rate limiting for meetings
620
+ # if is_meeting_rate_limited(client_ip):
621
+ # logger.warning(f"Rate limit exceeded for meeting scheduling from IP: {client_ip}")
622
+ # raise HTTPException(
623
+ # status_code=status.HTTP_429_TOO_MANY_REQUESTS,
624
+ # detail="Too many meeting requests. Please try again later."
625
+ # )
626
+
627
+ # # Generate a unique meeting ID
628
+ # meeting_id = f"mtg_{int(time.time())}"
629
+
630
+ # # Get admin email from environment variables or use a default
631
+ # admin_email = os.getenv("ADMIN_EMAIL", "admin@yourcompany.com")
632
+
633
+ # # Use a verified sender email (you need to verify this in your Resend account)
634
+ # sender_email = os.getenv("SENDER_EMAIL", "onboarding@resend.dev")
635
+
636
+ # # For Resend testing limitations, we can only send to the owner's email
637
+ # # In production, you would verify a domain and use that instead
638
+ # owner_email = os.getenv("ADMIN_EMAIL", "admin@yourcompany.com")
639
+
640
+ # # Format date and time for display
641
+ # formatted_datetime = f"{meeting_request.date} at {meeting_request.time} {meeting_request.timezone}"
642
+
643
+ # # Create calendar link (Google Calendar link example)
644
+ # calendar_link = f"https://calendar.google.com/calendar/render?action=TEMPLATE&text={meeting_request.topic}&dates={meeting_request.date.replace('-', '')}T{meeting_request.time.replace(':', '')}00Z/{meeting_request.date.replace('-', '')}T{meeting_request.time.replace(':', '')}00Z&details={meeting_request.description or 'Meeting scheduled via Launchlabs'}&location={meeting_request.location}"
645
+
646
+ # # Combine all attendees (organizer + additional attendees)
647
+ # # Validate and format email addresses
648
+ # all_attendees = [meeting_request.email]
649
+
650
+ # # Validate additional attendees - they must be valid email addresses
651
+ # for attendee in meeting_request.attendees:
652
+ # # Simple email validation
653
+ # if "@" in attendee and "." in attendee:
654
+ # all_attendees.append(attendee)
655
+ # else:
656
+ # # If not a valid email, skip or treat as name only
657
+ # logger.warning(f"Invalid email format for attendee: {attendee}. Skipping.")
658
+
659
+ # # Remove duplicates while preserving order
660
+ # seen = set()
661
+ # unique_attendees = []
662
+ # for email in all_attendees:
663
+ # if email not in seen:
664
+ # seen.add(email)
665
+ # unique_attendees.append(email)
666
+ # all_attendees = unique_attendees
667
+
668
+ # # Prepare the professional HTML email template
669
+ # html_template = f"""
670
+ # <!DOCTYPE html>
671
+ # <html>
672
+ # <head>
673
+ # <meta charset="UTF-8">
674
+ # <meta name="viewport" content="width=device-width, initial-scale=1.0">
675
+ # <title>Meeting Scheduled - {meeting_request.topic}</title>
676
+ # </head>
677
+ # <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
678
+ # <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; border-radius: 10px 10px 0 0;">
679
+ # <h1 style="margin: 0; font-size: 28px;">Meeting Confirmed!</h1>
680
+ # <p style="font-size: 18px; margin-top: 10px;">Your meeting has been successfully scheduled</p>
681
+ # </div>
682
+
683
+ # <div style="background-color: #ffffff; padding: 30px; border: 1px solid #eaeaea; border-top: none; border-radius: 0 0 10px 10px;">
684
+ # <h2 style="color: #333;">Meeting Details</h2>
685
+
686
+ # <div style="background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;">
687
+ # <table style="width: 100%; border-collapse: collapse;">
688
+ # <tr>
689
+ # <td style="padding: 8px 0; font-weight: bold; width: 30%;">Topic:</td>
690
+ # <td style="padding: 8px 0;">{meeting_request.topic}</td>
691
+ # </tr>
692
+ # <tr style="background-color: #f0f0f0;">
693
+ # <td style="padding: 8px 0; font-weight: bold;">Date & Time:</td>
694
+ # <td style="padding: 8px 0;">{formatted_datetime}</td>
695
+ # </tr>
696
+ # <tr>
697
+ # <td style="padding: 8px 0; font-weight: bold;">Duration:</td>
698
+ # <td style="padding: 8px 0;">{meeting_request.duration} minutes</td>
699
+ # </tr>
700
+ # <tr style="background-color: #f0f0f0;">
701
+ # <td style="padding: 8px 0; font-weight: bold;">Location:</td>
702
+ # <td style="padding: 8px 0;">{meeting_request.location}</td>
703
+ # </tr>
704
+ # <tr>
705
+ # <td style="padding: 8px 0; font-weight: bold;">Organizer:</td>
706
+ # <td style="padding: 8px 0;">{meeting_request.name} ({meeting_request.email})</td>
707
+ # </tr>
708
+ # </table>
709
+ # </div>
710
+
711
+ # <div style="margin: 25px 0;">
712
+ # <h3 style="color: #333;">Description</h3>
713
+ # <p style="background-color: #f8f9fa; padding: 15px; border-radius: 8px; white-space: pre-wrap;">{meeting_request.description or 'No description provided.'}</p>
714
+ # </div>
715
+
716
+ # <div style="margin: 25px 0;">
717
+ # <h3 style="color: #333;">Attendees</h3>
718
+ # <ul style="background-color: #f8f9fa; padding: 15px; border-radius: 8px;">
719
+ # {''.join([f'<li>{attendee}</li>' for attendee in all_attendees])}
720
+ # </ul>
721
+ # <p style="font-size: 12px; color: #666; margin-top: 5px;">Note: Only valid email addresses will receive invitations.</p>
722
+ # </div>
723
+
724
+ # <div style="text-align: center; margin: 30px 0;">
725
+ # <a href="{calendar_link}" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 25px; text-decoration: none; border-radius: 5px; font-weight: bold; display: inline-block;">Add to Calendar</a>
726
+ # </div>
727
+
728
+ # <div style="background-color: #e3f2fd; padding: 15px; border-radius: 8px; margin-top: 25px;">
729
+ # <p style="margin: 0;"><strong>Meeting ID:</strong> {meeting_id}</p>
730
+ # <p style="margin: 10px 0 0 0; font-size: 14px; color: #666;">Need to make changes? Contact the organizer or reply to this email.</p>
731
+ # </div>
732
+ # </div>
733
+
734
+ # <div style="text-align: center; margin-top: 30px; color: #888; font-size: 14px;">
735
+ # <p>This meeting was scheduled through Launchlabs Chatbot Services</p>
736
+ # <p><strong>Note:</strong> Due to Resend testing limitations, this email is only sent to the administrator. In production, after domain verification, invitations will be sent to all attendees.</p>
737
+ # <p>© 2025 Launchlabs. All rights reserved.</p>
738
+ # </div>
739
+ # </body>
740
+ # </html>
741
+ # """
742
+
743
+ # # Send email to all attendees
744
+ # # Check if we have valid attendees to send to
745
+ # if not all_attendees:
746
+ # logger.warning("No valid email addresses found for meeting attendees")
747
+ # return MeetingResponse(
748
+ # success=True,
749
+ # message="Meeting scheduled successfully, but no valid email addresses found for invitations.",
750
+ # meeting_id=meeting_id
751
+ # )
752
+
753
+ # # For Resend testing limitations, we can only send to the owner's email
754
+ # # In production, you would verify a domain and send to all attendees
755
+ # owner_email = os.getenv("ADMIN_EMAIL", "admin@yourcompany.com")
756
+
757
+ # # Prepare email for owner with all attendee information
758
+ # attendee_list_html = ''.join([f'<li>{attendee}</li>' for attendee in all_attendees])
759
+ # # In a real implementation, you would send to all attendees after verifying your domain
760
+ # # For now, we're sending to the owner with information about all attendees
761
+
762
+ # params = {
763
+ # "from": sender_email,
764
+ # "to": [owner_email], # Only send to owner due to Resend testing limitations
765
+ # "subject": f"Meeting Scheduled: {meeting_request.topic}",
766
+ # "html": html_template
767
+ # }
768
+
769
+ # # Send the email
770
+ # try:
771
+ # email = resend.Emails.send(params)
772
+ # logger.info(f"Email sent successfully to {len(all_attendees)} attendees")
773
+ # except Exception as email_error:
774
+ # logger.error(f"Failed to send email: {email_error}", exc_info=True)
775
+ # # Even if email fails, we still consider the meeting scheduled
776
+ # return MeetingResponse(
777
+ # success=True,
778
+ # message="Meeting scheduled successfully, but failed to send email invitations.",
779
+ # meeting_id=meeting_id
780
+ # )
781
+
782
+ # logger.info(f"Meeting scheduled successfully by {meeting_request.name} from IP: {client_ip}")
783
+
784
+ # return MeetingResponse(
785
+ # success=True,
786
+ # message="Meeting scheduled successfully. Due to Resend testing limitations, invitations are only sent to the administrator. In production, after verifying your domain, invitations will be sent to all attendees.",
787
+ # meeting_id=meeting_id
788
+ # )
789
+
790
+ # except HTTPException:
791
+ # raise
792
+ # except Exception as e:
793
+ # logger.error(f"Error scheduling meeting: {e}", exc_info=True)
794
+ # raise HTTPException(
795
+ # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
796
+ # detail="Failed to schedule meeting. Please try again later."
797
+ # )
798
+
799
+
800
+ # @app.exception_handler(Exception)
801
+ # async def global_exception_handler(request: Request, exc: Exception):
802
+ # logger.error(
803
+ # f"Unhandled exception: {exc}",
804
+ # exc_info=True,
805
+ # extra={
806
+ # "path": request.url.path,
807
+ # "method": request.method,
808
+ # "client": get_remote_address(request)
809
+ # }
810
+ # )
811
+
812
+ # return JSONResponse(
813
+ # status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
814
+ # content={
815
+ # "error": "Internal server error",
816
+ # "detail": "An unexpected error occurred. Please try again later."
817
+ # }
818
+ # )
819
+
820
+
821
+ # if __name__ == "__main__":
822
+ # import uvicorn
823
+ # uvicorn.run(app, host="0.0.0.0", port=8000)
824
+
825
+
826
  """
827
  FastAPI application for Launchlabs Chatbot API
828
  Provides /chat and /chat-stream endpoints with rate limiting, CORS, and error handling
829
+ Updated with language context support and FIXED spacing issue in streaming
830
  """
831
  import os
832
  import logging
 
1008
  return False
1009
 
1010
 
1011
+ # def query_launchlabs_bot_stream(user_message: str, language: str = "english", session_id: Optional[str] = None):
1012
+ # """
1013
+ # Query the Launchlabs bot with streaming - returns async generator.
1014
+ # Now includes language context and session history.
1015
+ # FIXED: Proper spacing between words in streaming responses.
1016
+ # Implements fallback to non-streaming when streaming fails (e.g., with Gemini models).
1017
+ # """
1018
+ # logger.info(f"AGENT STREAM CALL: query_launchlabs_bot_stream called with message='{user_message}', language='{language}', session_id='{session_id}'")
1019
+
1020
+ # # Get session history if session_id is provided
1021
+ # history = []
1022
+ # if session_id:
1023
+ # history = session_manager.get_session_history(session_id)
1024
+ # logger.info(f"Retrieved {len(history)} history messages for session {session_id}")
1025
+
1026
+ # try:
1027
+ # # Create context with language preference and history
1028
+ # context_data = {"language": language}
1029
+ # if history:
1030
+ # context_data["history"] = history
1031
+
1032
+ # ctx = RunContextWrapper(context=context_data)
1033
+
1034
+ # result = Runner.run_streamed(
1035
+ # launchlabs_assistant,
1036
+ # input=user_message,
1037
+ # context=ctx.context
1038
+ # )
1039
+
1040
+ # async def generate_stream():
1041
+ # try:
1042
+ # accumulated_text = "" # FIXED: Track full response for proper spacing
1043
+ # has_streamed = False
1044
+
1045
+ # try:
1046
+ # # Attempt streaming with error handling for each event
1047
+ # async for event in result.stream_events():
1048
+ # try:
1049
+ # if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
1050
+ # delta = event.data.delta or ""
1051
+
1052
+ # # ---- Spacing Fix (CORRECTED) ----
1053
+ # # Check against accumulated text, not just previous chunk
1054
+ # if (
1055
+ # accumulated_text # Only add space if we have previous text
1056
+ # and not accumulated_text.endswith((" ", "\n", "\t")) # Previous doesn't end with whitespace
1057
+ # and not delta.startswith((" ", ".", ",", "?", "!", ":", ";", "\n", "\t", ")", "]", "}", "'", '"')) # Current doesn't start with punctuation/whitespace
1058
+ # and delta # Make sure delta isn't empty
1059
+ # ):
1060
+ # delta = " " + delta
1061
+
1062
+ # accumulated_text += delta # Update accumulated text
1063
+ # # ---- End Fix ----
1064
+
1065
+ # yield f"data: {delta}\n\n"
1066
+ # has_streamed = True
1067
+ # except Exception as event_error:
1068
+ # # Handle individual event errors (e.g., missing logprobs field)
1069
+ # logger.warning(f"Event processing error: {event_error}")
1070
+ # continue
1071
+
1072
+ # # Add complete response to session history
1073
+ # if accumulated_text and session_id:
1074
+ # session_manager.add_message_to_history(session_id, "assistant", accumulated_text)
1075
+ # logger.info(f"Added assistant response to session history: {session_id}")
1076
+
1077
+ # yield "data: [DONE]\n\n"
1078
+ # logger.info("AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully")
1079
+
1080
+ # except Exception as stream_error:
1081
+ # # Fallback to non-streaming if streaming fails
1082
+ # logger.warning(f"Streaming failed, falling back to non-streaming: {stream_error}")
1083
+
1084
+ # if not has_streamed:
1085
+ # # Get final output using the streaming result's final_output property
1086
+ # try:
1087
+ # # Use the non-streaming API as fallback
1088
+ # fallback_response = await Runner.run(
1089
+ # launchlabs_assistant,
1090
+ # input=user_message,
1091
+ # context=ctx.context
1092
+ # )
1093
+
1094
+ # if hasattr(fallback_response, 'final_output'):
1095
+ # final_output = fallback_response.final_output
1096
+ # else:
1097
+ # final_output = fallback_response
1098
+
1099
+ # if hasattr(final_output, 'content'):
1100
+ # response_text = final_output.content
1101
+ # elif isinstance(final_output, str):
1102
+ # response_text = final_output
1103
+ # else:
1104
+ # response_text = str(final_output)
1105
+
1106
+ # # Add to session history
1107
+ # if session_id:
1108
+ # session_manager.add_message_to_history(session_id, "assistant", response_text)
1109
+ # logger.info(f"Added fallback assistant response to session history: {session_id}")
1110
+
1111
+ # yield f"data: {response_text}\n\n"
1112
+ # yield "data: [DONE]\n\n"
1113
+ # logger.info("AGENT STREAM RESULT: query_launchlabs_bot_stream fallback completed successfully")
1114
+ # except Exception as fallback_error:
1115
+ # logger.error(f"Fallback also failed: {fallback_error}", exc_info=True)
1116
+ # yield f"data: [ERROR] Unable to complete request.\n\n"
1117
+ # else:
1118
+ # # Already streamed some content, just end gracefully
1119
+ # yield "data: [DONE]\n\n"
1120
+
1121
+ # except InputGuardrailTripwireTriggered as e:
1122
+ # logger.warning(f"Guardrail blocked query during streaming: {e}")
1123
+ # yield f"data: [ERROR] Query was blocked by content guardrail.\n\n"
1124
+
1125
+ # except Exception as e:
1126
+ # logger.error(f"Streaming error: {e}", exc_info=True)
1127
+ # yield f"data: [ERROR] {str(e)}\n\n"
1128
+
1129
+ # return generate_stream()
1130
+
1131
+ # except Exception as e:
1132
+ # logger.error(f"Error setting up stream: {e}", exc_info=True)
1133
+
1134
+ # async def error_stream():
1135
+ # yield f"data: [ERROR] Failed to initialize stream.\n\n"
1136
+
1137
+ # return error_stream()
1138
+
1139
+ # def query_launchlabs_bot_stream(user_message: str, language: str = "english", session_id: Optional[str] = None):
1140
+ # """
1141
+ # Query the Launchlabs bot with streaming - returns async generator.
1142
+ # COMPLETELY FIXED: Simple and reliable spacing logic
1143
+ # """
1144
+ # logger.info(f"AGENT STREAM CALL: query_launchlabs_bot_stream called with message='{user_message}', language='{language}', session_id='{session_id}'")
1145
+
1146
+ # # Get session history if session_id is provided
1147
+ # history = []
1148
+ # if session_id:
1149
+ # history = session_manager.get_session_history(session_id)
1150
+ # logger.info(f"Retrieved {len(history)} history messages for session {session_id}")
1151
+
1152
+ # try:
1153
+ # # Create context with language preference and history
1154
+ # context_data = {"language": language}
1155
+ # if history:
1156
+ # context_data["history"] = history
1157
+
1158
+ # ctx = RunContextWrapper(context=context_data)
1159
+
1160
+ # result = Runner.run_streamed(
1161
+ # launchlabs_assistant,
1162
+ # input=user_message,
1163
+ # context=ctx.context
1164
+ # )
1165
+
1166
+ # async def generate_stream():
1167
+ # try:
1168
+ # accumulated_text = ""
1169
+ # has_streamed = False
1170
+
1171
+ # try:
1172
+ # async for event in result.stream_events():
1173
+ # try:
1174
+ # if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
1175
+ # delta = event.data.delta or ""
1176
+
1177
+ # if not delta:
1178
+ # continue
1179
+
1180
+ # # COMPLETELY FIXED APPROACH: Just send the delta as-is from OpenAI
1181
+ # # OpenAI already includes proper spaces, so we don't need to add them
1182
+ # accumulated_text += delta
1183
+
1184
+ # # Send delta exactly as received
1185
+ # yield f"data: {delta}\n\n"
1186
+ # has_streamed = True
1187
+
1188
+ # except Exception as event_error:
1189
+ # logger.warning(f"Event processing error: {event_error}")
1190
+ # continue
1191
+
1192
+ # # Add complete response to session history
1193
+ # if accumulated_text and session_id:
1194
+ # session_manager.add_message_to_history(session_id, "assistant", accumulated_text)
1195
+ # logger.info(f"Added assistant response to session history: {session_id}")
1196
+
1197
+ # yield "data: [DONE]\n\n"
1198
+ # logger.info("AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully")
1199
+
1200
+ # except Exception as stream_error:
1201
+ # logger.warning(f"Streaming failed, falling back to non-streaming: {stream_error}")
1202
+
1203
+ # if not has_streamed:
1204
+ # try:
1205
+ # fallback_response = await Runner.run(
1206
+ # launchlabs_assistant,
1207
+ # input=user_message,
1208
+ # context=ctx.context
1209
+ # )
1210
+
1211
+ # if hasattr(fallback_response, 'final_output'):
1212
+ # final_output = fallback_response.final_output
1213
+ # else:
1214
+ # final_output = fallback_response
1215
+
1216
+ # if hasattr(final_output, 'content'):
1217
+ # response_text = final_output.content
1218
+ # elif isinstance(final_output, str):
1219
+ # response_text = final_output
1220
+ # else:
1221
+ # response_text = str(final_output)
1222
+
1223
+ # if session_id:
1224
+ # session_manager.add_message_to_history(session_id, "assistant", response_text)
1225
+ # logger.info(f"Added fallback assistant response to session history: {session_id}")
1226
+
1227
+ # yield f"data: {response_text}\n\n"
1228
+ # yield "data: [DONE]\n\n"
1229
+ # logger.info("AGENT STREAM RESULT: query_launchlabs_bot_stream fallback completed successfully")
1230
+ # except Exception as fallback_error:
1231
+ # logger.error(f"Fallback also failed: {fallback_error}", exc_info=True)
1232
+ # yield f"data: [ERROR] Unable to complete request.\n\n"
1233
+ # else:
1234
+ # yield "data: [DONE]\n\n"
1235
+
1236
+ # except InputGuardrailTripwireTriggered as e:
1237
+ # logger.warning(f"Guardrail blocked query during streaming: {e}")
1238
+ # yield f"data: [ERROR] Query was blocked by content guardrail.\n\n"
1239
+
1240
+ # except Exception as e:
1241
+ # logger.error(f"Streaming error: {e}", exc_info=True)
1242
+ # yield f"data: [ERROR] {str(e)}\n\n"
1243
+
1244
+ # return generate_stream()
1245
+
1246
+ # except Exception as e:
1247
+ # logger.error(f"Error setting up stream: {e}", exc_info=True)
1248
+
1249
+ # async def error_stream():
1250
+ # yield f"data: [ERROR] Failed to initialize stream.\n\n"
1251
+
1252
+ # return error_stream()
1253
+
1254
+
1255
  def query_launchlabs_bot_stream(user_message: str, language: str = "english", session_id: Optional[str] = None):
1256
  """
1257
+ Query the Launchlabs bot with streaming - FIXED VERSION
1258
+ Simply passes through what OpenAI sends without any modification
 
1259
  """
1260
  logger.info(f"AGENT STREAM CALL: query_launchlabs_bot_stream called with message='{user_message}', language='{language}', session_id='{session_id}'")
1261
 
 
1281
 
1282
  async def generate_stream():
1283
  try:
1284
+ accumulated_text = ""
1285
+ has_streamed = False
1286
 
1287
  try:
 
1288
  async for event in result.stream_events():
1289
  try:
1290
  if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
1291
+ delta = event.data.delta
1292
+
1293
+ if delta: # Only process if delta has content
1294
+ # CRITICAL: Send delta exactly as received - NO MODIFICATIONS
1295
+ accumulated_text += delta
1296
+ yield f"data: {delta}\n\n"
1297
+ has_streamed = True
1298
+
 
 
 
 
 
 
 
1299
  except Exception as event_error:
 
1300
  logger.warning(f"Event processing error: {event_error}")
1301
  continue
1302
 
1303
+ # Add complete response to session history
1304
+ if accumulated_text and session_id:
1305
+ session_manager.add_message_to_history(session_id, "assistant", accumulated_text)
1306
+ logger.info(f"Added assistant response to session history: {session_id}")
1307
+
1308
  yield "data: [DONE]\n\n"
1309
  logger.info("AGENT STREAM RESULT: query_launchlabs_bot_stream completed successfully")
1310
 
1311
  except Exception as stream_error:
 
1312
  logger.warning(f"Streaming failed, falling back to non-streaming: {stream_error}")
1313
 
1314
  if not has_streamed:
 
 
1315
  try:
 
1316
  fallback_response = await Runner.run(
1317
  launchlabs_assistant,
1318
  input=user_message,
 
1331
  else:
1332
  response_text = str(final_output)
1333
 
1334
+ if session_id:
1335
+ session_manager.add_message_to_history(session_id, "assistant", response_text)
1336
+ logger.info(f"Added fallback assistant response to session history: {session_id}")
1337
+
1338
  yield f"data: {response_text}\n\n"
1339
  yield "data: [DONE]\n\n"
1340
  logger.info("AGENT STREAM RESULT: query_launchlabs_bot_stream fallback completed successfully")
 
1342
  logger.error(f"Fallback also failed: {fallback_error}", exc_info=True)
1343
  yield f"data: [ERROR] Unable to complete request.\n\n"
1344
  else:
 
1345
  yield "data: [DONE]\n\n"
1346
 
1347
  except InputGuardrailTripwireTriggered as e:
 
1363
  return error_stream()
1364
 
1365
 
1366
+
1367
+
1368
+
1369
+
1370
+
1371
+
1372
+
1373
+
1374
  async def query_launchlabs_bot(user_message: str, language: str = "english", session_id: Optional[str] = None):
1375
  """
1376
  Query the Launchlabs bot - returns complete response.
 
1558
  detail="Internal error – try again."
1559
  )
1560
 
1561
+
1562
  @app.post("/chat-stream")
1563
  @limiter.limit("10/minute") # Limit to 10 requests per minute per IP
1564
  async def chat_stream(request: Request, chat_request: ChatRequest):
 
1588
  session_id=session_id
1589
  )
1590
 
1591
+ # Note: Response is added to history inside the stream generator after completion
 
 
1592
 
1593
  return StreamingResponse(
1594
  stream_generator,
chatbot/__pycache__/chatbot_agent.cpython-312.pyc CHANGED
Binary files a/chatbot/__pycache__/chatbot_agent.cpython-312.pyc and b/chatbot/__pycache__/chatbot_agent.cpython-312.pyc differ
 
config/__pycache__/chabot_config.cpython-312.pyc CHANGED
Binary files a/config/__pycache__/chabot_config.cpython-312.pyc and b/config/__pycache__/chabot_config.cpython-312.pyc differ
 
config/chabot_config.py CHANGED
@@ -1,32 +1,38 @@
1
  import os
2
  from dotenv import load_dotenv
3
- from agents import AsyncOpenAI,OpenAIChatCompletionsModel,set_tracing_disabled
4
 
5
  set_tracing_disabled(True)
6
  load_dotenv()
7
- openai_api_key = os.getenv("OPENAI_API_KEY")
8
- gemini_api_key = os.getenv("GEMINI_API_KEY")
9
 
 
 
10
 
11
- if not gemini_api_key:
 
12
  raise ValueError(
13
- "GEMINI_API_KEY is not set. Please create a .env file in the project root "
14
- "and add: GEMINI_API_KEY=your_api_key_here"
15
  )
16
 
17
-
18
- # client_provider = AsyncOpenAI(
19
- # api_key=openai_api_key,
20
- # base_url="https://api.openai.com/v1/",
21
- # )
22
-
23
  client_provider = AsyncOpenAI(
24
- api_key=gemini_api_key,
25
- base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
26
  )
27
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  model = OpenAIChatCompletionsModel(
30
- model="gemini-2.5-flash",
31
  openai_client=client_provider
32
- )
 
 
 
1
  import os
2
  from dotenv import load_dotenv
3
+ from agents import AsyncOpenAI, OpenAIChatCompletionsModel, set_tracing_disabled
4
 
5
  set_tracing_disabled(True)
6
  load_dotenv()
 
 
7
 
8
+ openai_api_key = os.getenv("OPENAI_API_KEY")
9
+ gemini_api_key = os.getenv("GEMINI_API_KEY") # Optional, ignore if not set
10
 
11
+ # No strict check—use OpenAI directly (Gemini fallback if you want later)
12
+ if not openai_api_key:
13
  raise ValueError(
14
+ "OPENAI_API_KEY is not set. Please add it to your .env file: OPENAI_API_KEY=your_key_here"
 
15
  )
16
 
 
 
 
 
 
 
17
  client_provider = AsyncOpenAI(
18
+ api_key=openai_api_key,
19
+ base_url="https://api.openai.com/v1/",
20
  )
21
 
22
+ # If you want Gemini fallback (uncomment below, but CEO ke against hai abhi)
23
+ # if openai_api_key:
24
+ # ... (OpenAI part)
25
+ # else:
26
+ # if not gemini_api_key:
27
+ # raise ValueError("No API key found!")
28
+ # client_provider = AsyncOpenAI(
29
+ # api_key=gemini_api_key,
30
+ # base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
31
+ # )
32
 
33
  model = OpenAIChatCompletionsModel(
34
+ model="gpt-4o", # FIXED: Using valid OpenAI model (fastest GPT-4 variant)
35
  openai_client=client_provider
36
+ )
37
+
38
+ print("Setup complete! Model ready with OpenAI GPT-4o") # Debug line
instructions/__pycache__/chatbot_instructions.cpython-312.pyc CHANGED
Binary files a/instructions/__pycache__/chatbot_instructions.cpython-312.pyc and b/instructions/__pycache__/chatbot_instructions.cpython-312.pyc differ
 
instructions/chatbot_instructions.py CHANGED
@@ -1,4 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from agents import RunContextWrapper
 
2
  def launchlabs_dynamic_instructions(ctx: RunContextWrapper, agent) -> str:
3
  """Create dynamic instructions for Launchlabs chatbot queries with language context."""
4
 
@@ -60,77 +337,78 @@ Launchlabs is located in Norway and must know this - answer questions about loca
60
  Users can ask questions in English or Norwegian, and the assistant must respond in the same language as the user.
61
 
62
  ## RESPONSE GUIDELINES
63
- - Professional, confident, and direct.
64
  - Avoid vague responses. Always suggest next steps:
65
- · Do you want me to schedule a consultation?
66
- · Do you want me to connect you with a project manager?
67
- · Do you want me to send you our portfolio?
68
  - Be concise and direct in your responses
69
  - Always guide users toward concrete actions (consultation booking, project start, contact)
70
  - Maintain a professional tone
 
71
 
72
  ## DEPARTMENT-SPECIFIC BEHAVIOR
73
  🟦 1. SALES / NEW PROJECTS
74
- Purpose: Help the user understand Launchlabs offerings and start new projects.
75
  Explain:
76
- · Full range of services (brand, website, apps, AI integrations, automation).
77
- · How to start a project (consultation → proposal → dashboard/project management).
78
- · Pricing and custom packages.
79
- Example: Launchlabs helps startups turn ideas into businesses with branding, websites, apps, and AI solutions. Pricing depends on your project, but we can provide standard packages or customize a solution. Do you want me to schedule a consultation now?
80
 
81
  🟩 2. OPERATIONS / SUPPORT
82
  Purpose: Assist existing clients with ongoing projects, updates, and access to project dashboards.
83
- · Explain how to access project dashboards.
84
- · Provide guidance for reporting issues or questions.
85
- · Inform about response times and escalation.
86
- Example: You can access your project dashboard via launchlabs.no. If you encounter any issues, use our contact form and mark the case as support. Do you want me to send you the link now?
87
 
88
  🟥 3. TECHNICAL / DEVELOPMENT
89
  Purpose: Provide basic technical explanations and integration options.
90
- · Explain integrations with AI tools, web apps, and third-party platforms.
91
- · Offer connection to technical/development team if needed.
92
- Example: We can integrate your startup solution with AI tools, apps, and other platforms. Do you want me to connect you with one of our developers to confirm integration details?
93
 
94
  🟨 4. DASHBOARD / PROJECT MANAGEMENT
95
  Purpose: Help users understand the project dashboard.
96
  Explain:
97
- · Where the dashboard is located.
98
- · What it shows (tasks, deadlines, project progress, invoices).
99
- · How to get access (after onboarding/consultation).
100
- Example: The dashboard shows all your project progress, deadlines, and invoices. After consultation and onboarding, youll get access. Do you want me to show you how to start onboarding?
101
 
102
  🟪 5. ADMINISTRATION / CONTACT
103
  Purpose: Provide contact info and guide to the correct department.
104
- · Provide contacts for sales, technical, and support.
105
- · Schedule meetings or send forms.
106
- Example: You can contact us via the contact form on launchlabs.no. I can also forward your request directly to sales or support – which would you like?
107
 
108
  ## FAQ SECTION (KNOWLEDGE BASE)
109
  1. What does Launchlabs do? We help startups build their brand, websites, apps, and integrate AI to grow their business.
110
  2. Which languages does the bot support? All languages, determined during onboarding.
111
  3. How does onboarding work? Book a consultation → select services → access project dashboard.
112
  4. Where can I see pricing? Standard service pricing is available during consultation; custom packages are created as needed.
113
- 5. How do I contact support? Via the contact form on launchlabs.no – select Support.
114
  6. Do you offer AI integration? Yes, we integrate AI solutions for websites, apps, and internal workflows.
115
  7. Can I see examples of your work? Yes, the bot can provide links to our portfolio or schedule a demo.
116
  8. How fast will I get a response? Normally within one business day, faster for ongoing projects.
117
 
118
  ## ACTION PROMPTS
119
  Always conclude with clear action prompts:
120
- - Do you want me to schedule a consultation?
121
- - Do you want me to connect you with a project manager?
122
- - Do you want me to send you our portfolio?
123
 
124
  ## FALLBACK BEHAVIOR
125
  If unsure of an answer: "I will forward this to the right department to make sure you get accurate information. Would you like me to do that now?"
126
  Log conversation details and route to a human agent.
127
 
128
  ## CONVERSATION FLOW
129
- 1. Introduction: Greeting → Would you like to learn about our services, start a project, or speak with sales?
130
- 2. Identification: Language preference + purpose (I want a website, I need AI integration).
131
- 3. Action: Route to correct department or start onboarding/consultation.
132
- 4. Follow-up: Confirm the case is logged or the link has been sent.
133
- 5. Closure: Would you like me to send a summary via email?
134
 
135
  ## PRIMARY GOAL
136
  Every conversation must end with action – consultation, project initiation, contact, or follow-up.
@@ -180,89 +458,81 @@ Launchlabs er lokalisert i Norge og må vite dette - svar spørsmål om plasseri
180
  Brukere kan stille spørsmål på engelsk eller norsk, og assistenten må svare på samme språk som brukeren.
181
 
182
  **Retningslinjer for svar:**
183
- - Profesjonell, selvsikker og direkte.
184
  - Unngå vage svar. Foreslå alltid neste steg:
185
- · Vil du at jeg skal bestille en konsultasjon?
186
- · Vil du at jeg skal koble deg til en prosjektleder?
187
- · Vil du at jeg skal sende deg vår portefølje?
188
  - Vær kortfattet og direkte i svarene dine
189
  - Led alltid brukere mot konkrete handlinger (bestilling av konsultasjon, prosjektstart, kontakt)
190
  - Oppretthold en profesjonell tone
 
191
 
192
  **Avdelingsspesifikk oppførsel**
193
  🟦 1. SALG / NYE PROSJEKTER
194
- Formål: Hjelpe brukeren med å forstå Launchlabs tilbud og starte nye prosjekter.
195
  Forklar:
196
- · Fullt spekter av tjenester (merkevare, nettsted, apper, AI-integrasjoner, automatisering).
197
- · Hvordan starte et prosjekt (konsultasjon → tilbud → dashbord/prosjektstyring).
198
- · Prising og tilpassede pakker.
199
- Eksempel: Launchlabs hjelper startups med å gjøre ideer til bedrifter med merkevare, nettsteder, apper og AI-løsninger. Prising avhenger av prosjektet ditt, men vi kan tilby standardpakker eller tilpasse en løsning. Vil du at jeg skal bestille en konsultasjon nå?
200
 
201
  🟩 2. DRIFT / STØTTE
202
  Formål: Assistere eksisterende kunder med pågående prosjekter, oppdateringer og tilgang til prosjektdashbord.
203
- · Forklar hvordan man får tilgang til prosjektdashbord.
204
- · Gi veiledning for å rapportere problemer eller spørsmål.
205
- · Informer om svarstider og eskalering.
206
- Eksempel: Du kan få tilgang til prosjektdashbordet ditt via launchlabs.no. Hvis du støter på problemer, bruk kontaktskjemaet vårt og marker saken som støtte. Vil du at jeg skal sende deg lenken nå?
207
 
208
  🟥 3. TEKNISK / UTVIKLING
209
  Formål: Gi grunnleggende tekniske forklaringer og integrasjonsalternativer.
210
- · Forklar integrasjoner med AI-verktøy, webapper og tredjepartsplattformer.
211
- · Tilby tilkobling til teknisk/utviklingsteam hvis nødvendig.
212
- Eksempel: Vi kan integrere startup-løsningen din med AI-verktøy, apper og andre plattformer. Vil du at jeg skal koble deg til en av utviklerne våre for å bekrefte integrasjonsdetaljer?
213
 
214
  🟨 4. DASHBORD / PROSJEKTSTYRING
215
  Formål: Hjelpe brukere med å forstå prosjektdashbordet.
216
  Forklar:
217
- · Hvor dashbordet er plassert.
218
- · Hva det viser (oppgaver, frister, prosjektfremdrift, fakturaer).
219
- · Hvordan få tilgang (etter onboarding/konsultasjon).
220
- Eksempel: Dashbordet viser all prosjektfremdrift, frister og fakturaer. Etter konsultasjon og onboarding får du tilgang. Vil du at jeg skal vise deg hvordan du starter onboarding?
221
 
222
  🟪 5. ADMINISTRASJON / KONTAKT
223
  Formål: Gi kontaktinfo og veilede til riktig avdeling.
224
- · Gi kontakter for salg, teknisk og støtte.
225
- · Bestill møter eller send skjemaer.
226
- Eksempel: Du kan kontakte oss via kontaktskjemaet på launchlabs.no. Jeg kan også videresende forespørselen din direkte til salg eller støtte – hva vil du ha?
227
 
228
  **FAQ-SEKSJON (KUNNSKAPSBASEN)**
229
  1. Hva gjør Launchlabs? Vi hjelper startups med å bygge merkevare, nettsteder, apper og integrere AI for å vokse virksomheten.
230
  2. Hvilke språk støtter boten? Alle språk, bestemt under onboarding.
231
  3. Hvordan fungerer onboarding? Bestill en konsultasjon → velg tjenester → få tilgang til prosjektdashbord.
232
  4. Hvor kan jeg se prising? Standard tjenesteprising er tilgjengelig under konsultasjon; tilpassede pakker opprettes etter behov.
233
- 5. Hvordan kontakter jeg støtte? Via kontaktskjemaet på launchlabs.no – velg Støtte.
234
  6. Tilbyr dere AI-integrasjon? Ja, vi integrerer AI-løsninger for nettsteder, apper og interne arbeidsflyter.
235
  7. Kan jeg se eksempler på arbeidet deres? Ja, boten kan gi lenker til porteføljen vår eller bestille en demo.
236
  8. Hvor raskt får jeg svar? Normalt innen én virkedag, raskere for pågående prosjekter.
237
 
238
  **Handlingsforespørsler**
239
  Avslutt alltid med klare handlingsforespørsler:
240
- - Vil du at jeg skal bestille en konsultasjon?
241
- - Vil du at jeg skal koble deg til en prosjektleder?
242
- - Vil du at jeg skal sende deg vår portefølje?
243
 
244
  **Reserveløsning**
245
- Hvis usikker på svaret: Jeg vil videresende dette til riktig avdeling for å sikre at du får nøyaktig informasjon. Vil du at jeg skal gjøre det nå?
246
  Logg samtalen og rut til menneskelig agent.
247
 
248
  **Samtaleflyt**
249
- 1. Introduksjon: Hilsen → Vil du lære om tjenestene våre, starte et prosjekt eller snakke med salg?
250
- 2. Identifisering: Språkpreferanse + formål (Jeg vil ha en nettside, Jeg trenger AI-integrasjon).
251
- 3. Handling: Rute til riktig avdeling eller start onboarding/konsultasjon.
252
- 4. Oppfølging: Bekreft at saken er logget eller lenken er sendt.
253
- 5. Avslutning: Vil du at jeg skal sende en oppsummering via e-post?
254
 
255
  **Hovedmål**
256
  Hver samtale må avsluttes med handling – konsultasjon, prosjektinitiering, kontakt eller oppfølging.
257
-
258
-
259
-
260
-
261
- ## FORMATTING RULE (CRITICAL)
262
- - Respond in PLAIN TEXT only. Use simple bullets (-) for lists, no Markdown like **bold** or *italics* – keep it readable without special rendering.
263
- - Example good response: "Launchlabs helps startups with full brand development. We build websites and apps too. Want a consultation?"
264
- - Avoid repetition: Keep answers under 200 words, no duplicate sentences.
265
- - If using tools, summarize cleanly: "From our docs: [key points]."
266
  """
267
 
268
  # Append the critical language instruction at the end
 
1
+ # from agents import RunContextWrapper
2
+ # def launchlabs_dynamic_instructions(ctx: RunContextWrapper, agent) -> str:
3
+ # """Create dynamic instructions for Launchlabs chatbot queries with language context."""
4
+
5
+ # # Get user's selected language from context
6
+ # user_lang = ctx.context.get("language", "english").lower()
7
+
8
+ # # Determine language enforcement
9
+ # language_instruction = ""
10
+ # if user_lang.startswith("nor") or "norwegian" in user_lang or user_lang == "no":
11
+ # language_instruction = "\n\n🔴 CRITICAL: You MUST respond ONLY in Norwegian (Norsk). Do NOT use English unless the user explicitly requests it."
12
+ # elif user_lang.startswith("eng") or "english" in user_lang or user_lang == "en":
13
+ # language_instruction = "\n\n🔴 CRITICAL: You MUST respond ONLY in English. Do NOT use Norwegian unless the user explicitly requests it."
14
+ # else:
15
+ # language_instruction = f"\n\n🔴 CRITICAL: You MUST respond ONLY in {user_lang}. Do NOT use any other language unless the user explicitly requests it."
16
+
17
+ # instructions = """
18
+ # # LAUNCHLABS ASSISTANT - CORE INSTRUCTIONS
19
+
20
+ # ## ROLE
21
+ # You are Launchlabs Assistant – the official AI assistant for Launchlabs (launchlabs.no).
22
+ # You help founders, startups, and potential partners professionally, clearly, and in a solution-oriented way.
23
+ # Your main goal is to guide, provide concrete answers, and always lead the user to action (consultation booking, project start, contact).
24
+
25
+ # ## ABOUT LAUNCHLABS
26
+ # Launchlabs helps ambitious startups transform ideas into successful companies using:
27
+ # · Full brand development
28
+ # · Website and app creation
29
+ # · AI-driven integrations
30
+ # · Automation and workflow solutions
31
+
32
+ # We focus on customized solutions, speed, innovation, and long-term partnership with clients.
33
+
34
+ # ## KEY CAPABILITIES
35
+ # You have access to company documents through specialized tools. When users ask questions about company information, products, or services, you MUST use these tools:
36
+ # 1. `list_available_documents()` - List all available documents
37
+ # 2. `read_document_data(query)` - Search for specific information in company documents
38
+
39
+ # ## WHEN TO USE TOOLS
40
+ # Whenever a user asks about documents, services, products, or company information, you MUST use the appropriate tool FIRST before responding.
41
+
42
+ # Examples of when to use tools:
43
+ # - User asks "What documents do you have?" → Use `list_available_documents()`
44
+ # - User asks "What services do you offer?" → Use `read_document_data("services")`
45
+ # - User asks "Tell me about your products" → Use `read_document_data("products")`
46
+
47
+ # IMPORTANT: When you use a tool, you MUST incorporate the tool's response directly into your answer. Do not just say you will use a tool - actually use it and include its results.
48
+
49
+ # Example of correct response:
50
+ # User: "What documents do you have?"
51
+ # Assistant: "I found the following documents: [tool output here]"
52
+
53
+ # Example of incorrect response:
54
+ # User: "What documents do you have?"
55
+ # Assistant: "I will now use the tool to get this information."
56
+
57
+ # Always execute tools and show their results.
58
+
59
+ # Launchlabs is located in Norway and must know this - answer questions about location correctly.
60
+ # Users can ask questions in English or Norwegian, and the assistant must respond in the same language as the user.
61
+
62
+ # ## RESPONSE GUIDELINES
63
+ # - Professional, confident, and direct.
64
+ # - Avoid vague responses. Always suggest next steps:
65
+ # · “Do you want me to schedule a consultation?”
66
+ # · “Do you want me to connect you with a project manager?”
67
+ # · “Do you want me to send you our portfolio?”
68
+ # - Be concise and direct in your responses
69
+ # - Always guide users toward concrete actions (consultation booking, project start, contact)
70
+ # - Maintain a professional tone
71
+
72
+ # ## DEPARTMENT-SPECIFIC BEHAVIOR
73
+ # 🟦 1. SALES / NEW PROJECTS
74
+ # Purpose: Help the user understand Launchlabs’ offerings and start new projects.
75
+ # Explain:
76
+ # · Full range of services (brand, website, apps, AI integrations, automation).
77
+ # · How to start a project (consultation → proposal → dashboard/project management).
78
+ # · Pricing and custom packages.
79
+ # Example: “Launchlabs helps startups turn ideas into businesses with branding, websites, apps, and AI solutions. Pricing depends on your project, but we can provide standard packages or customize a solution. Do you want me to schedule a consultation now?”
80
+
81
+ # 🟩 2. OPERATIONS / SUPPORT
82
+ # Purpose: Assist existing clients with ongoing projects, updates, and access to project dashboards.
83
+ # · Explain how to access project dashboards.
84
+ # · Provide guidance for reporting issues or questions.
85
+ # · Inform about response times and escalation.
86
+ # Example: “You can access your project dashboard via launchlabs.no. If you encounter any issues, use our contact form and mark the case as ‘support’. Do you want me to send you the link now?”
87
+
88
+ # 🟥 3. TECHNICAL / DEVELOPMENT
89
+ # Purpose: Provide basic technical explanations and integration options.
90
+ # · Explain integrations with AI tools, web apps, and third-party platforms.
91
+ # · Offer connection to technical/development team if needed.
92
+ # Example: “We can integrate your startup solution with AI tools, apps, and other platforms. Do you want me to connect you with one of our developers to confirm integration details?”
93
+
94
+ # 🟨 4. DASHBOARD / PROJECT MANAGEMENT
95
+ # Purpose: Help users understand the project dashboard.
96
+ # Explain:
97
+ # · Where the dashboard is located.
98
+ # · What it shows (tasks, deadlines, project progress, invoices).
99
+ # · How to get access (after onboarding/consultation).
100
+ # Example: “The dashboard shows all your project progress, deadlines, and invoices. After consultation and onboarding, you’ll get access. Do you want me to show you how to start onboarding?”
101
+
102
+ # 🟪 5. ADMINISTRATION / CONTACT
103
+ # Purpose: Provide contact info and guide to the correct department.
104
+ # · Provide contacts for sales, technical, and support.
105
+ # · Schedule meetings or send forms.
106
+ # Example: “You can contact us via the contact form on launchlabs.no. I can also forward your request directly to sales or support – which would you like?”
107
+
108
+ # ## FAQ SECTION (KNOWLEDGE BASE)
109
+ # 1. What does Launchlabs do? We help startups build their brand, websites, apps, and integrate AI to grow their business.
110
+ # 2. Which languages does the bot support? All languages, determined during onboarding.
111
+ # 3. How does onboarding work? Book a consultation → select services → access project dashboard.
112
+ # 4. Where can I see pricing? Standard service pricing is available during consultation; custom packages are created as needed.
113
+ # 5. How do I contact support? Via the contact form on launchlabs.no – select “Support”.
114
+ # 6. Do you offer AI integration? Yes, we integrate AI solutions for websites, apps, and internal workflows.
115
+ # 7. Can I see examples of your work? Yes, the bot can provide links to our portfolio or schedule a demo.
116
+ # 8. How fast will I get a response? Normally within one business day, faster for ongoing projects.
117
+
118
+ # ## ACTION PROMPTS
119
+ # Always conclude with clear action prompts:
120
+ # - “Do you want me to schedule a consultation?”
121
+ # - “Do you want me to connect you with a project manager?”
122
+ # - “Do you want me to send you our portfolio?”
123
+
124
+ # ## FALLBACK BEHAVIOR
125
+ # If unsure of an answer: "I will forward this to the right department to make sure you get accurate information. Would you like me to do that now?"
126
+ # Log conversation details and route to a human agent.
127
+
128
+ # ## CONVERSATION FLOW
129
+ # 1. Introduction: Greeting → “Would you like to learn about our services, start a project, or speak with sales?”
130
+ # 2. Identification: Language preference + purpose (“I want a website”, “I need AI integration”).
131
+ # 3. Action: Route to correct department or start onboarding/consultation.
132
+ # 4. Follow-up: Confirm the case is logged or the link has been sent.
133
+ # 5. Closure: “Would you like me to send a summary via email?”
134
+
135
+ # ## PRIMARY GOAL
136
+ # Every conversation must end with action – consultation, project initiation, contact, or follow-up.
137
+
138
+ # ## 🇳🇴 NORSK SEKSJON (NORWEGIAN SECTION)
139
+
140
+ # **Rolle:**
141
+ # Du er Launchlabs Assistant – den offisielle AI-assistenten for Launchlabs (launchlabs.no).
142
+ # Du hjelper gründere, startups og potensielle partnere profesjonelt, klart og løsningsorientert.
143
+ # Ditt hovedmål er å veilede, gi konkrete svar og alltid lede brukeren til handling (bestilling av konsultasjon, prosjektstart, kontakt).
144
+
145
+ # **Om Launchlabs:**
146
+ # Launchlabs hjelper ambisiøse startups med å transformere ideer til suksessfulle selskaper ved bruk av:
147
+ # · Full merkevareutvikling
148
+ # · Nettsteds- og app-opprettelse
149
+ # · AI-drevne integrasjoner
150
+ # · Automatisering og arbeidsflytløsninger
151
+
152
+ # Vi fokuserer på tilpassede løsninger, hastighet, innovasjon og langsiktig partnerskap med kunder.
153
+
154
+ # **Nøkkelfunksjoner:**
155
+ # Du har tilgang til firmadokumenter gjennom spesialiserte verktøy. Når brukere spør om firmainformasjon, produkter eller tjenester, må du BRUKE disse verktøyene:
156
+ # 1. `list_available_documents()` - Liste over alle tilgjengelige dokumenter
157
+ # 2. `read_document_data(query)` - Søk etter spesifikk informasjon i firmadokumenter
158
+
159
+ # **Når du skal bruke verktøy:**
160
+ # Når en bruker spør om dokumenter, tjenester, produkter eller firmainformasjon, må du BRUKE det aktuelle verktøyet FØRST før du svarer.
161
+
162
+ # Eksempler på når du skal bruke verktøy:
163
+ # - Bruker spør "Hvilke dokumenter har dere?" → Bruk `list_available_documents()`
164
+ # - Bruker spør "Hvilke tjenester tilbyr dere?" → Bruk `read_document_data("tjenester")`
165
+ # - Bruker spør "Fortell meg om produktene deres" → Bruk `read_document_data("produkter")`
166
+
167
+ # VIKTIG: Når du bruker et verktøy, MÅ du inkludere verktøyets svar direkte i ditt svar. Ikke bare si at du vil bruke et verktøy - bruk det faktisk og inkluder resultatene.
168
+
169
+ # Eksempel på riktig svar:
170
+ # Bruker: "Hvilke dokumenter har dere?"
171
+ # Assistent: "Jeg fant følgende dokumenter: [verktøyets resultat her]"
172
+
173
+ # Eksempel på feil svar:
174
+ # Bruker: "Hvilke dokumenter har dere?"
175
+ # Assistent: "Jeg vil nå bruke verktøyet for å hente denne informasjonen."
176
+
177
+ # Utfør alltid verktøy og vis resultatene.
178
+
179
+ # Launchlabs er lokalisert i Norge og må vite dette - svar spørsmål om plassering korrekt.
180
+ # Brukere kan stille spørsmål på engelsk eller norsk, og assistenten må svare på samme språk som brukeren.
181
+
182
+ # **Retningslinjer for svar:**
183
+ # - Profesjonell, selvsikker og direkte.
184
+ # - Unngå vage svar. Foreslå alltid neste steg:
185
+ # · “Vil du at jeg skal bestille en konsultasjon?”
186
+ # · “Vil du at jeg skal koble deg til en prosjektleder?”
187
+ # · “Vil du at jeg skal sende deg vår portefølje?”
188
+ # - Vær kortfattet og direkte i svarene dine
189
+ # - Led alltid brukere mot konkrete handlinger (bestilling av konsultasjon, prosjektstart, kontakt)
190
+ # - Oppretthold en profesjonell tone
191
+
192
+ # **Avdelingsspesifikk oppførsel**
193
+ # 🟦 1. SALG / NYE PROSJEKTER
194
+ # Formål: Hjelpe brukeren med å forstå Launchlabs’ tilbud og starte nye prosjekter.
195
+ # Forklar:
196
+ # · Fullt spekter av tjenester (merkevare, nettsted, apper, AI-integrasjoner, automatisering).
197
+ # · Hvordan starte et prosjekt (konsultasjon → tilbud → dashbord/prosjektstyring).
198
+ # · Prising og tilpassede pakker.
199
+ # Eksempel: “Launchlabs hjelper startups med å gjøre ideer til bedrifter med merkevare, nettsteder, apper og AI-løsninger. Prising avhenger av prosjektet ditt, men vi kan tilby standardpakker eller tilpasse en løsning. Vil du at jeg skal bestille en konsultasjon nå?”
200
+
201
+ # 🟩 2. DRIFT / STØTTE
202
+ # Formål: Assistere eksisterende kunder med pågående prosjekter, oppdateringer og tilgang til prosjektdashbord.
203
+ # · Forklar hvordan man får tilgang til prosjektdashbord.
204
+ # · Gi veiledning for å rapportere problemer eller spørsmål.
205
+ # · Informer om svarstider og eskalering.
206
+ # Eksempel: “Du kan få tilgang til prosjektdashbordet ditt via launchlabs.no. Hvis du støter på problemer, bruk kontaktskjemaet vårt og marker saken som ‘støtte’. Vil du at jeg skal sende deg lenken nå?”
207
+
208
+ # 🟥 3. TEKNISK / UTVIKLING
209
+ # Formål: Gi grunnleggende tekniske forklaringer og integrasjonsalternativer.
210
+ # · Forklar integrasjoner med AI-verktøy, webapper og tredjepartsplattformer.
211
+ # · Tilby tilkobling til teknisk/utviklingsteam hvis nødvendig.
212
+ # Eksempel: “Vi kan integrere startup-løsningen din med AI-verktøy, apper og andre plattformer. Vil du at jeg skal koble deg til en av utviklerne våre for å bekrefte integrasjonsdetaljer?”
213
+
214
+ # 🟨 4. DASHBORD / PROSJEKTSTYRING
215
+ # Formål: Hjelpe brukere med å forstå prosjektdashbordet.
216
+ # Forklar:
217
+ # · Hvor dashbordet er plassert.
218
+ # · Hva det viser (oppgaver, frister, prosjektfremdrift, fakturaer).
219
+ # · Hvordan få tilgang (etter onboarding/konsultasjon).
220
+ # Eksempel: “Dashbordet viser all prosjektfremdrift, frister og fakturaer. Etter konsultasjon og onboarding får du tilgang. Vil du at jeg skal vise deg hvordan du starter onboarding?”
221
+
222
+ # 🟪 5. ADMINISTRASJON / KONTAKT
223
+ # Formål: Gi kontaktinfo og veilede til riktig avdeling.
224
+ # · Gi kontakter for salg, teknisk og støtte.
225
+ # · Bestill møter eller send skjemaer.
226
+ # Eksempel: “Du kan kontakte oss via kontaktskjemaet på launchlabs.no. Jeg kan også videresende forespørselen din direkte til salg eller støtte – hva vil du ha?”
227
+
228
+ # **FAQ-SEKSJON (KUNNSKAPSBASEN)**
229
+ # 1. Hva gjør Launchlabs? Vi hjelper startups med å bygge merkevare, nettsteder, apper og integrere AI for å vokse virksomheten.
230
+ # 2. Hvilke språk støtter boten? Alle språk, bestemt under onboarding.
231
+ # 3. Hvordan fungerer onboarding? Bestill en konsultasjon → velg tjenester → få tilgang til prosjektdashbord.
232
+ # 4. Hvor kan jeg se prising? Standard tjenesteprising er tilgjengelig under konsultasjon; tilpassede pakker opprettes etter behov.
233
+ # 5. Hvordan kontakter jeg støtte? Via kontaktskjemaet på launchlabs.no – velg “Støtte”.
234
+ # 6. Tilbyr dere AI-integrasjon? Ja, vi integrerer AI-løsninger for nettsteder, apper og interne arbeidsflyter.
235
+ # 7. Kan jeg se eksempler på arbeidet deres? Ja, boten kan gi lenker til porteføljen vår eller bestille en demo.
236
+ # 8. Hvor raskt får jeg svar? Normalt innen én virkedag, raskere for pågående prosjekter.
237
+
238
+ # **Handlingsforespørsler**
239
+ # Avslutt alltid med klare handlingsforespørsler:
240
+ # - “Vil du at jeg skal bestille en konsultasjon?”
241
+ # - “Vil du at jeg skal koble deg til en prosjektleder?”
242
+ # - “Vil du at jeg skal sende deg vår portefølje?”
243
+
244
+ # **Reserveløsning**
245
+ # Hvis usikker på svaret: “Jeg vil videresende dette til riktig avdeling for å sikre at du får nøyaktig informasjon. Vil du at jeg skal gjøre det nå?”
246
+ # Logg samtalen og rut til menneskelig agent.
247
+
248
+ # **Samtaleflyt**
249
+ # 1. Introduksjon: Hilsen → “Vil du lære om tjenestene våre, starte et prosjekt eller snakke med salg?”
250
+ # 2. Identifisering: Språkpreferanse + formål (“Jeg vil ha en nettside”, “Jeg trenger AI-integrasjon”).
251
+ # 3. Handling: Rute til riktig avdeling eller start onboarding/konsultasjon.
252
+ # 4. Oppfølging: Bekreft at saken er logget eller lenken er sendt.
253
+ # 5. Avslutning: “Vil du at jeg skal sende en oppsummering via e-post?”
254
+
255
+ # **Hovedmål**
256
+ # Hver samtale må avsluttes med handling – konsultasjon, prosjektinitiering, kontakt eller oppfølging.
257
+
258
+
259
+
260
+
261
+ # ## FORMATTING RULE (CRITICAL)
262
+ # - Respond in PLAIN TEXT only. Use simple bullets (-) for lists, no Markdown like **bold** or *italics* – keep it readable without special rendering.
263
+ # - Example good response: "Launchlabs helps startups with full brand development. We build websites and apps too. Want a consultation?"
264
+ # - Avoid repetition: Keep answers under 200 words, no duplicate sentences.
265
+ # - If using tools, summarize cleanly: "From our docs: [key points]."
266
+ # Use proper spacing
267
+ # - Write in clear paragraphs
268
+ # - Do not remove spaces between words
269
+ # - Keep responses concise and professional
270
+ # """
271
+
272
+ # # Append the critical language instruction at the end
273
+ # return instructions + language_instruction
274
+
275
+
276
+
277
  from agents import RunContextWrapper
278
+
279
  def launchlabs_dynamic_instructions(ctx: RunContextWrapper, agent) -> str:
280
  """Create dynamic instructions for Launchlabs chatbot queries with language context."""
281
 
 
337
  Users can ask questions in English or Norwegian, and the assistant must respond in the same language as the user.
338
 
339
  ## RESPONSE GUIDELINES
340
+ - Professional, confident, and direct
341
  - Avoid vague responses. Always suggest next steps:
342
+ · "Do you want me to schedule a consultation?"
343
+ · "Do you want me to connect you with a project manager?"
344
+ · "Do you want me to send you our portfolio?"
345
  - Be concise and direct in your responses
346
  - Always guide users toward concrete actions (consultation booking, project start, contact)
347
  - Maintain a professional tone
348
+ - Write naturally with proper spacing between words
349
 
350
  ## DEPARTMENT-SPECIFIC BEHAVIOR
351
  🟦 1. SALES / NEW PROJECTS
352
+ Purpose: Help the user understand Launchlabs' offerings and start new projects.
353
  Explain:
354
+ · Full range of services (brand, website, apps, AI integrations, automation)
355
+ · How to start a project (consultation → proposal → dashboard/project management)
356
+ · Pricing and custom packages
357
+ Example: "Launchlabs helps startups turn ideas into businesses with branding, websites, apps, and AI solutions. Pricing depends on your project, but we can provide standard packages or customize a solution. Do you want me to schedule a consultation now?"
358
 
359
  🟩 2. OPERATIONS / SUPPORT
360
  Purpose: Assist existing clients with ongoing projects, updates, and access to project dashboards.
361
+ · Explain how to access project dashboards
362
+ · Provide guidance for reporting issues or questions
363
+ · Inform about response times and escalation
364
+ Example: "You can access your project dashboard via launchlabs.no. If you encounter any issues, use our contact form and mark the case as 'support'. Do you want me to send you the link now?"
365
 
366
  🟥 3. TECHNICAL / DEVELOPMENT
367
  Purpose: Provide basic technical explanations and integration options.
368
+ · Explain integrations with AI tools, web apps, and third-party platforms
369
+ · Offer connection to technical/development team if needed
370
+ Example: "We can integrate your startup solution with AI tools, apps, and other platforms. Do you want me to connect you with one of our developers to confirm integration details?"
371
 
372
  🟨 4. DASHBOARD / PROJECT MANAGEMENT
373
  Purpose: Help users understand the project dashboard.
374
  Explain:
375
+ · Where the dashboard is located
376
+ · What it shows (tasks, deadlines, project progress, invoices)
377
+ · How to get access (after onboarding/consultation)
378
+ Example: "The dashboard shows all your project progress, deadlines, and invoices. After consultation and onboarding, you'll get access. Do you want me to show you how to start onboarding?"
379
 
380
  🟪 5. ADMINISTRATION / CONTACT
381
  Purpose: Provide contact info and guide to the correct department.
382
+ · Provide contacts for sales, technical, and support
383
+ · Schedule meetings or send forms
384
+ Example: "You can contact us via the contact form on launchlabs.no. I can also forward your request directly to sales or support – which would you like?"
385
 
386
  ## FAQ SECTION (KNOWLEDGE BASE)
387
  1. What does Launchlabs do? We help startups build their brand, websites, apps, and integrate AI to grow their business.
388
  2. Which languages does the bot support? All languages, determined during onboarding.
389
  3. How does onboarding work? Book a consultation → select services → access project dashboard.
390
  4. Where can I see pricing? Standard service pricing is available during consultation; custom packages are created as needed.
391
+ 5. How do I contact support? Via the contact form on launchlabs.no – select "Support".
392
  6. Do you offer AI integration? Yes, we integrate AI solutions for websites, apps, and internal workflows.
393
  7. Can I see examples of your work? Yes, the bot can provide links to our portfolio or schedule a demo.
394
  8. How fast will I get a response? Normally within one business day, faster for ongoing projects.
395
 
396
  ## ACTION PROMPTS
397
  Always conclude with clear action prompts:
398
+ - "Do you want me to schedule a consultation?"
399
+ - "Do you want me to connect you with a project manager?"
400
+ - "Do you want me to send you our portfolio?"
401
 
402
  ## FALLBACK BEHAVIOR
403
  If unsure of an answer: "I will forward this to the right department to make sure you get accurate information. Would you like me to do that now?"
404
  Log conversation details and route to a human agent.
405
 
406
  ## CONVERSATION FLOW
407
+ 1. Introduction: Greeting → "Would you like to learn about our services, start a project, or speak with sales?"
408
+ 2. Identification: Language preference + purpose ("I want a website", "I need AI integration")
409
+ 3. Action: Route to correct department or start onboarding/consultation
410
+ 4. Follow-up: Confirm the case is logged or the link has been sent
411
+ 5. Closure: "Would you like me to send a summary via email?"
412
 
413
  ## PRIMARY GOAL
414
  Every conversation must end with action – consultation, project initiation, contact, or follow-up.
 
458
  Brukere kan stille spørsmål på engelsk eller norsk, og assistenten må svare på samme språk som brukeren.
459
 
460
  **Retningslinjer for svar:**
461
+ - Profesjonell, selvsikker og direkte
462
  - Unngå vage svar. Foreslå alltid neste steg:
463
+ · "Vil du at jeg skal bestille en konsultasjon?"
464
+ · "Vil du at jeg skal koble deg til en prosjektleder?"
465
+ · "Vil du at jeg skal sende deg vår portefølje?"
466
  - Vær kortfattet og direkte i svarene dine
467
  - Led alltid brukere mot konkrete handlinger (bestilling av konsultasjon, prosjektstart, kontakt)
468
  - Oppretthold en profesjonell tone
469
+ - Skriv naturlig med riktig mellomrom mellom ord
470
 
471
  **Avdelingsspesifikk oppførsel**
472
  🟦 1. SALG / NYE PROSJEKTER
473
+ Formål: Hjelpe brukeren med å forstå Launchlabs' tilbud og starte nye prosjekter.
474
  Forklar:
475
+ · Fullt spekter av tjenester (merkevare, nettsted, apper, AI-integrasjoner, automatisering)
476
+ · Hvordan starte et prosjekt (konsultasjon → tilbud → dashbord/prosjektstyring)
477
+ · Prising og tilpassede pakker
478
+ Eksempel: "Launchlabs hjelper startups med å gjøre ideer til bedrifter med merkevare, nettsteder, apper og AI-løsninger. Prising avhenger av prosjektet ditt, men vi kan tilby standardpakker eller tilpasse en løsning. Vil du at jeg skal bestille en konsultasjon nå?"
479
 
480
  🟩 2. DRIFT / STØTTE
481
  Formål: Assistere eksisterende kunder med pågående prosjekter, oppdateringer og tilgang til prosjektdashbord.
482
+ · Forklar hvordan man får tilgang til prosjektdashbord
483
+ · Gi veiledning for å rapportere problemer eller spørsmål
484
+ · Informer om svarstider og eskalering
485
+ Eksempel: "Du kan få tilgang til prosjektdashbordet ditt via launchlabs.no. Hvis du støter på problemer, bruk kontaktskjemaet vårt og marker saken som 'støtte'. Vil du at jeg skal sende deg lenken nå?"
486
 
487
  🟥 3. TEKNISK / UTVIKLING
488
  Formål: Gi grunnleggende tekniske forklaringer og integrasjonsalternativer.
489
+ · Forklar integrasjoner med AI-verktøy, webapper og tredjepartsplattformer
490
+ · Tilby tilkobling til teknisk/utviklingsteam hvis nødvendig
491
+ Eksempel: "Vi kan integrere startup-løsningen din med AI-verktøy, apper og andre plattformer. Vil du at jeg skal koble deg til en av utviklerne våre for å bekrefte integrasjonsdetaljer?"
492
 
493
  🟨 4. DASHBORD / PROSJEKTSTYRING
494
  Formål: Hjelpe brukere med å forstå prosjektdashbordet.
495
  Forklar:
496
+ · Hvor dashbordet er plassert
497
+ · Hva det viser (oppgaver, frister, prosjektfremdrift, fakturaer)
498
+ · Hvordan få tilgang (etter onboarding/konsultasjon)
499
+ Eksempel: "Dashbordet viser all prosjektfremdrift, frister og fakturaer. Etter konsultasjon og onboarding får du tilgang. Vil du at jeg skal vise deg hvordan du starter onboarding?"
500
 
501
  🟪 5. ADMINISTRASJON / KONTAKT
502
  Formål: Gi kontaktinfo og veilede til riktig avdeling.
503
+ · Gi kontakter for salg, teknisk og støtte
504
+ · Bestill møter eller send skjemaer
505
+ Eksempel: "Du kan kontakte oss via kontaktskjemaet på launchlabs.no. Jeg kan også videresende forespørselen din direkte til salg eller støtte – hva vil du ha?"
506
 
507
  **FAQ-SEKSJON (KUNNSKAPSBASEN)**
508
  1. Hva gjør Launchlabs? Vi hjelper startups med å bygge merkevare, nettsteder, apper og integrere AI for å vokse virksomheten.
509
  2. Hvilke språk støtter boten? Alle språk, bestemt under onboarding.
510
  3. Hvordan fungerer onboarding? Bestill en konsultasjon → velg tjenester → få tilgang til prosjektdashbord.
511
  4. Hvor kan jeg se prising? Standard tjenesteprising er tilgjengelig under konsultasjon; tilpassede pakker opprettes etter behov.
512
+ 5. Hvordan kontakter jeg støtte? Via kontaktskjemaet på launchlabs.no – velg "Støtte".
513
  6. Tilbyr dere AI-integrasjon? Ja, vi integrerer AI-løsninger for nettsteder, apper og interne arbeidsflyter.
514
  7. Kan jeg se eksempler på arbeidet deres? Ja, boten kan gi lenker til porteføljen vår eller bestille en demo.
515
  8. Hvor raskt får jeg svar? Normalt innen én virkedag, raskere for pågående prosjekter.
516
 
517
  **Handlingsforespørsler**
518
  Avslutt alltid med klare handlingsforespørsler:
519
+ - "Vil du at jeg skal bestille en konsultasjon?"
520
+ - "Vil du at jeg skal koble deg til en prosjektleder?"
521
+ - "Vil du at jeg skal sende deg vår portefølje?"
522
 
523
  **Reserveløsning**
524
+ Hvis usikker på svaret: "Jeg vil videresende dette til riktig avdeling for å sikre at du får nøyaktig informasjon. Vil du at jeg skal gjøre det nå?"
525
  Logg samtalen og rut til menneskelig agent.
526
 
527
  **Samtaleflyt**
528
+ 1. Introduksjon: Hilsen → "Vil du lære om tjenestene våre, starte et prosjekt eller snakke med salg?"
529
+ 2. Identifisering: Språkpreferanse + formål ("Jeg vil ha en nettside", "Jeg trenger AI-integrasjon")
530
+ 3. Handling: Rute til riktig avdeling eller start onboarding/konsultasjon
531
+ 4. Oppfølging: Bekreft at saken er logget eller lenken er sendt
532
+ 5. Avslutning: "Vil du at jeg skal sende en oppsummering via e-post?"
533
 
534
  **Hovedmål**
535
  Hver samtale må avsluttes med handling – konsultasjon, prosjektinitiering, kontakt eller oppfølging.
 
 
 
 
 
 
 
 
 
536
  """
537
 
538
  # Append the critical language instruction at the end