khoj 1.22.3.dev5__py3-none-any.whl → 1.23.3.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- khoj/database/adapters/__init__.py +8 -3
- khoj/database/migrations/0061_alter_chatmodeloptions_model_type.py +26 -0
- khoj/database/migrations/0061_alter_texttoimagemodelconfig_model_type.py +21 -0
- khoj/database/migrations/0062_merge_20240913_0222.py +14 -0
- khoj/database/models/__init__.py +2 -0
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/{page-3c01900e7b5c7e50.js → page-1ac024e05374f91f.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/{page-6ea3381528603372.js → page-85e9176b460c5e33.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-ababf339318a3b50.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/factchecker/{page-04a19ab1a988976f.js → page-21cf46aca7e6d487.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/{page-95ecd0acac7ece82.js → page-b406302925829b15.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/search/{page-fa15807b1ad7e30b.js → page-fde8c956cc33a187.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/{page-1a2acc46cdabaf4a.js → page-88737126debb4712.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/{page-e20f54450d3ce6c0.js → page-f11b4fb0f2bc3381.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/{webpack-07fad5db87344b82.js → webpack-f162a207b26413cd.js} +1 -1
- khoj/interface/compiled/_next/static/css/17e284bae7dc4881.css +1 -0
- khoj/interface/compiled/_next/static/css/37a313cb39403a84.css +1 -0
- khoj/interface/compiled/_next/static/css/4cae6c0e5c72fb2d.css +1 -0
- khoj/interface/compiled/_next/static/css/6bde1f2045622ef7.css +1 -0
- khoj/interface/compiled/agents/index.html +1 -1
- khoj/interface/compiled/agents/index.txt +2 -2
- khoj/interface/compiled/automations/index.html +1 -1
- khoj/interface/compiled/automations/index.txt +2 -2
- khoj/interface/compiled/chat/index.html +1 -1
- khoj/interface/compiled/chat/index.txt +2 -2
- khoj/interface/compiled/factchecker/index.html +1 -1
- khoj/interface/compiled/factchecker/index.txt +2 -2
- khoj/interface/compiled/index.html +1 -1
- khoj/interface/compiled/index.txt +2 -2
- khoj/interface/compiled/search/index.html +1 -1
- khoj/interface/compiled/search/index.txt +2 -2
- khoj/interface/compiled/settings/index.html +1 -1
- khoj/interface/compiled/settings/index.txt +2 -2
- khoj/interface/compiled/share/chat/index.html +1 -1
- khoj/interface/compiled/share/chat/index.txt +2 -2
- khoj/interface/email/magic_link.html +1 -1
- khoj/interface/email/task.html +1 -1
- khoj/interface/email/welcome.html +1 -1
- khoj/processor/conversation/google/__init__.py +0 -0
- khoj/processor/conversation/google/gemini_chat.py +221 -0
- khoj/processor/conversation/google/utils.py +192 -0
- khoj/processor/conversation/openai/gpt.py +2 -0
- khoj/processor/conversation/openai/utils.py +35 -10
- khoj/processor/conversation/prompts.py +6 -6
- khoj/processor/conversation/utils.py +16 -5
- khoj/processor/image/generate.py +212 -0
- khoj/processor/tools/online_search.py +2 -1
- khoj/routers/api.py +13 -0
- khoj/routers/api_chat.py +1 -1
- khoj/routers/email.py +6 -1
- khoj/routers/helpers.py +86 -164
- {khoj-1.22.3.dev5.dist-info → khoj-1.23.3.dev1.dist-info}/METADATA +2 -1
- {khoj-1.22.3.dev5.dist-info → khoj-1.23.3.dev1.dist-info}/RECORD +61 -54
- khoj/interface/compiled/_next/static/chunks/app/chat/page-132e5199f954559f.js +0 -1
- khoj/interface/compiled/_next/static/css/149c5104fe3d38b8.css +0 -1
- khoj/interface/compiled/_next/static/css/2272c73fc7a3b571.css +0 -1
- khoj/interface/compiled/_next/static/css/553f9cdcc7a2bcd6.css +0 -1
- khoj/interface/compiled/_next/static/css/a3530ec58b0b660f.css +0 -1
- /khoj/interface/compiled/_next/static/{vjWGo1xJFCitZUk51rujk → BtK3cBCv0oGm04ZdaAvMB}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{vjWGo1xJFCitZUk51rujk → BtK3cBCv0oGm04ZdaAvMB}/_ssgManifest.js +0 -0
- /khoj/interface/compiled/_next/static/chunks/{8423-ce22327cf2d2edae.js → 8423-14fc72aec9104ce9.js} +0 -0
- /khoj/interface/compiled/_next/static/chunks/{9178-3a0baad1c172d515.js → 9178-c153fc402c970365.js} +0 -0
- /khoj/interface/compiled/_next/static/chunks/{9417-2e54c6fd056982d8.js → 9417-5d14ac74aaab2c66.js} +0 -0
- {khoj-1.22.3.dev5.dist-info → khoj-1.23.3.dev1.dist-info}/WHEEL +0 -0
- {khoj-1.22.3.dev5.dist-info → khoj-1.23.3.dev1.dist-info}/entry_points.txt +0 -0
- {khoj-1.22.3.dev5.dist-info → khoj-1.23.3.dev1.dist-info}/licenses/LICENSE +0 -0
khoj/routers/helpers.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
import asyncio
|
2
|
-
import base64
|
3
2
|
import hashlib
|
4
|
-
import io
|
5
3
|
import json
|
6
4
|
import logging
|
7
5
|
import math
|
@@ -16,7 +14,6 @@ from typing import (
|
|
16
14
|
Annotated,
|
17
15
|
Any,
|
18
16
|
AsyncGenerator,
|
19
|
-
Callable,
|
20
17
|
Dict,
|
21
18
|
Iterator,
|
22
19
|
List,
|
@@ -24,17 +21,15 @@ from typing import (
|
|
24
21
|
Tuple,
|
25
22
|
Union,
|
26
23
|
)
|
27
|
-
from urllib.parse import parse_qs,
|
24
|
+
from urllib.parse import parse_qs, urljoin, urlparse
|
28
25
|
|
29
26
|
import cron_descriptor
|
30
|
-
import openai
|
31
27
|
import pytz
|
32
28
|
import requests
|
33
29
|
from apscheduler.job import Job
|
34
30
|
from apscheduler.triggers.cron import CronTrigger
|
35
31
|
from asgiref.sync import sync_to_async
|
36
32
|
from fastapi import Depends, Header, HTTPException, Request, UploadFile
|
37
|
-
from PIL import Image
|
38
33
|
from starlette.authentication import has_required_scope
|
39
34
|
from starlette.requests import URL
|
40
35
|
|
@@ -76,6 +71,10 @@ from khoj.processor.conversation.anthropic.anthropic_chat import (
|
|
76
71
|
anthropic_send_message_to_model,
|
77
72
|
converse_anthropic,
|
78
73
|
)
|
74
|
+
from khoj.processor.conversation.google.gemini_chat import (
|
75
|
+
converse_gemini,
|
76
|
+
gemini_send_message_to_model,
|
77
|
+
)
|
79
78
|
from khoj.processor.conversation.offline.chat_model import (
|
80
79
|
converse_offline,
|
81
80
|
send_message_to_model_offline,
|
@@ -84,11 +83,11 @@ from khoj.processor.conversation.openai.gpt import converse, send_message_to_mod
|
|
84
83
|
from khoj.processor.conversation.utils import (
|
85
84
|
ThreadedGenerator,
|
86
85
|
generate_chatml_messages_with_context,
|
86
|
+
remove_json_codeblock,
|
87
87
|
save_to_conversation_log,
|
88
88
|
)
|
89
89
|
from khoj.processor.speech.text_to_speech import is_eleven_labs_enabled
|
90
90
|
from khoj.routers.email import is_resend_enabled, send_task_email
|
91
|
-
from khoj.routers.storage import upload_image
|
92
91
|
from khoj.routers.twilio import is_twilio_enabled
|
93
92
|
from khoj.search_type import text_search
|
94
93
|
from khoj.utils import state
|
@@ -96,8 +95,6 @@ from khoj.utils.config import OfflineChatProcessorModel
|
|
96
95
|
from khoj.utils.helpers import (
|
97
96
|
LRU,
|
98
97
|
ConversationCommand,
|
99
|
-
ImageIntentType,
|
100
|
-
convert_image_to_webp,
|
101
98
|
is_none_or_empty,
|
102
99
|
is_valid_url,
|
103
100
|
log_telemetry,
|
@@ -136,7 +133,7 @@ async def is_ready_to_chat(user: KhojUser):
|
|
136
133
|
await ConversationAdapters.aget_default_conversation_config()
|
137
134
|
)
|
138
135
|
|
139
|
-
if user_conversation_config and user_conversation_config.model_type ==
|
136
|
+
if user_conversation_config and user_conversation_config.model_type == ChatModelOptions.ModelType.OFFLINE:
|
140
137
|
chat_model = user_conversation_config.chat_model
|
141
138
|
max_tokens = user_conversation_config.max_prompt_size
|
142
139
|
if state.offline_chat_processor_config is None:
|
@@ -146,7 +143,14 @@ async def is_ready_to_chat(user: KhojUser):
|
|
146
143
|
|
147
144
|
if (
|
148
145
|
user_conversation_config
|
149
|
-
and (
|
146
|
+
and (
|
147
|
+
user_conversation_config.model_type
|
148
|
+
in [
|
149
|
+
ChatModelOptions.ModelType.OPENAI,
|
150
|
+
ChatModelOptions.ModelType.ANTHROPIC,
|
151
|
+
ChatModelOptions.ModelType.GOOGLE,
|
152
|
+
]
|
153
|
+
)
|
150
154
|
and user_conversation_config.openai_config
|
151
155
|
):
|
152
156
|
return True
|
@@ -287,9 +291,7 @@ async def aget_relevant_information_sources(
|
|
287
291
|
|
288
292
|
try:
|
289
293
|
response = response.strip()
|
290
|
-
|
291
|
-
if response.startswith("```json"):
|
292
|
-
response = response[7:-3]
|
294
|
+
response = remove_json_codeblock(response)
|
293
295
|
response = json.loads(response)
|
294
296
|
response = [q.strip() for q in response["source"] if q.strip()]
|
295
297
|
if not isinstance(response, list) or not response or len(response) == 0:
|
@@ -342,7 +344,9 @@ async def aget_relevant_output_modes(
|
|
342
344
|
response = await send_message_to_model_wrapper(relevant_mode_prompt, response_type="json_object")
|
343
345
|
|
344
346
|
try:
|
345
|
-
response =
|
347
|
+
response = response.strip()
|
348
|
+
response = remove_json_codeblock(response)
|
349
|
+
response = json.loads(response)
|
346
350
|
|
347
351
|
if is_none_or_empty(response):
|
348
352
|
return ConversationCommand.Text
|
@@ -422,9 +426,7 @@ async def generate_online_subqueries(
|
|
422
426
|
# Validate that the response is a non-empty, JSON-serializable list
|
423
427
|
try:
|
424
428
|
response = response.strip()
|
425
|
-
|
426
|
-
if response.startswith("```json") and response.endswith("```"):
|
427
|
-
response = response[7:-3]
|
429
|
+
response = remove_json_codeblock(response)
|
428
430
|
response = json.loads(response)
|
429
431
|
response = [q.strip() for q in response["queries"] if q.strip()]
|
430
432
|
if not isinstance(response, list) or not response or len(response) == 0:
|
@@ -558,7 +560,7 @@ async def generate_better_image_prompt(
|
|
558
560
|
references=user_references,
|
559
561
|
online_results=simplified_online_results,
|
560
562
|
)
|
561
|
-
elif model_type
|
563
|
+
elif model_type in [TextToImageModelConfig.ModelType.STABILITYAI, TextToImageModelConfig.ModelType.REPLICATE]:
|
562
564
|
image_prompt = prompts.image_generation_improve_prompt_sd.format(
|
563
565
|
query=q,
|
564
566
|
chat_history=conversation_history,
|
@@ -607,9 +609,10 @@ async def send_message_to_model_wrapper(
|
|
607
609
|
else conversation_config.max_prompt_size
|
608
610
|
)
|
609
611
|
tokenizer = conversation_config.tokenizer
|
612
|
+
model_type = conversation_config.model_type
|
610
613
|
vision_available = conversation_config.vision_enabled
|
611
614
|
|
612
|
-
if
|
615
|
+
if model_type == ChatModelOptions.ModelType.OFFLINE:
|
613
616
|
if state.offline_chat_processor_config is None or state.offline_chat_processor_config.loaded_model is None:
|
614
617
|
state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model, max_tokens)
|
615
618
|
|
@@ -633,7 +636,7 @@ async def send_message_to_model_wrapper(
|
|
633
636
|
response_type=response_type,
|
634
637
|
)
|
635
638
|
|
636
|
-
elif
|
639
|
+
elif model_type == ChatModelOptions.ModelType.OPENAI:
|
637
640
|
openai_chat_config = conversation_config.openai_config
|
638
641
|
api_key = openai_chat_config.api_key
|
639
642
|
api_base_url = openai_chat_config.api_base_url
|
@@ -657,7 +660,7 @@ async def send_message_to_model_wrapper(
|
|
657
660
|
)
|
658
661
|
|
659
662
|
return openai_response
|
660
|
-
elif
|
663
|
+
elif model_type == ChatModelOptions.ModelType.ANTHROPIC:
|
661
664
|
api_key = conversation_config.openai_config.api_key
|
662
665
|
truncated_messages = generate_chatml_messages_with_context(
|
663
666
|
user_message=message,
|
@@ -666,6 +669,7 @@ async def send_message_to_model_wrapper(
|
|
666
669
|
max_prompt_size=max_tokens,
|
667
670
|
tokenizer_name=tokenizer,
|
668
671
|
vision_enabled=vision_available,
|
672
|
+
uploaded_image_url=uploaded_image_url,
|
669
673
|
model_type=conversation_config.model_type,
|
670
674
|
)
|
671
675
|
|
@@ -674,6 +678,21 @@ async def send_message_to_model_wrapper(
|
|
674
678
|
api_key=api_key,
|
675
679
|
model=chat_model,
|
676
680
|
)
|
681
|
+
elif model_type == ChatModelOptions.ModelType.GOOGLE:
|
682
|
+
api_key = conversation_config.openai_config.api_key
|
683
|
+
truncated_messages = generate_chatml_messages_with_context(
|
684
|
+
user_message=message,
|
685
|
+
system_message=system_message,
|
686
|
+
model_name=chat_model,
|
687
|
+
max_prompt_size=max_tokens,
|
688
|
+
tokenizer_name=tokenizer,
|
689
|
+
vision_enabled=vision_available,
|
690
|
+
uploaded_image_url=uploaded_image_url,
|
691
|
+
)
|
692
|
+
|
693
|
+
return gemini_send_message_to_model(
|
694
|
+
messages=truncated_messages, api_key=api_key, model=chat_model, response_type=response_type
|
695
|
+
)
|
677
696
|
else:
|
678
697
|
raise HTTPException(status_code=500, detail="Invalid conversation config")
|
679
698
|
|
@@ -692,7 +711,7 @@ def send_message_to_model_wrapper_sync(
|
|
692
711
|
max_tokens = conversation_config.max_prompt_size
|
693
712
|
vision_available = conversation_config.vision_enabled
|
694
713
|
|
695
|
-
if conversation_config.model_type ==
|
714
|
+
if conversation_config.model_type == ChatModelOptions.ModelType.OFFLINE:
|
696
715
|
if state.offline_chat_processor_config is None or state.offline_chat_processor_config.loaded_model is None:
|
697
716
|
state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model, max_tokens)
|
698
717
|
|
@@ -714,7 +733,7 @@ def send_message_to_model_wrapper_sync(
|
|
714
733
|
response_type=response_type,
|
715
734
|
)
|
716
735
|
|
717
|
-
elif conversation_config.model_type ==
|
736
|
+
elif conversation_config.model_type == ChatModelOptions.ModelType.OPENAI:
|
718
737
|
api_key = conversation_config.openai_config.api_key
|
719
738
|
truncated_messages = generate_chatml_messages_with_context(
|
720
739
|
user_message=message,
|
@@ -730,7 +749,7 @@ def send_message_to_model_wrapper_sync(
|
|
730
749
|
|
731
750
|
return openai_response
|
732
751
|
|
733
|
-
elif conversation_config.model_type ==
|
752
|
+
elif conversation_config.model_type == ChatModelOptions.ModelType.ANTHROPIC:
|
734
753
|
api_key = conversation_config.openai_config.api_key
|
735
754
|
truncated_messages = generate_chatml_messages_with_context(
|
736
755
|
user_message=message,
|
@@ -746,6 +765,22 @@ def send_message_to_model_wrapper_sync(
|
|
746
765
|
api_key=api_key,
|
747
766
|
model=chat_model,
|
748
767
|
)
|
768
|
+
|
769
|
+
elif conversation_config.model_type == ChatModelOptions.ModelType.GOOGLE:
|
770
|
+
api_key = conversation_config.openai_config.api_key
|
771
|
+
truncated_messages = generate_chatml_messages_with_context(
|
772
|
+
user_message=message,
|
773
|
+
system_message=system_message,
|
774
|
+
model_name=chat_model,
|
775
|
+
max_prompt_size=max_tokens,
|
776
|
+
vision_enabled=vision_available,
|
777
|
+
)
|
778
|
+
|
779
|
+
return gemini_send_message_to_model(
|
780
|
+
messages=truncated_messages,
|
781
|
+
api_key=api_key,
|
782
|
+
model=chat_model,
|
783
|
+
)
|
749
784
|
else:
|
750
785
|
raise HTTPException(status_code=500, detail="Invalid conversation config")
|
751
786
|
|
@@ -811,7 +846,7 @@ def generate_chat_response(
|
|
811
846
|
agent=agent,
|
812
847
|
)
|
813
848
|
|
814
|
-
elif conversation_config.model_type ==
|
849
|
+
elif conversation_config.model_type == ChatModelOptions.ModelType.OPENAI:
|
815
850
|
openai_chat_config = conversation_config.openai_config
|
816
851
|
api_key = openai_chat_config.api_key
|
817
852
|
chat_model = conversation_config.chat_model
|
@@ -834,7 +869,7 @@ def generate_chat_response(
|
|
834
869
|
vision_available=vision_available,
|
835
870
|
)
|
836
871
|
|
837
|
-
elif conversation_config.model_type ==
|
872
|
+
elif conversation_config.model_type == ChatModelOptions.ModelType.ANTHROPIC:
|
838
873
|
api_key = conversation_config.openai_config.api_key
|
839
874
|
chat_response = converse_anthropic(
|
840
875
|
compiled_references,
|
@@ -851,6 +886,23 @@ def generate_chat_response(
|
|
851
886
|
user_name=user_name,
|
852
887
|
agent=agent,
|
853
888
|
)
|
889
|
+
elif conversation_config.model_type == ChatModelOptions.ModelType.GOOGLE:
|
890
|
+
api_key = conversation_config.openai_config.api_key
|
891
|
+
chat_response = converse_gemini(
|
892
|
+
compiled_references,
|
893
|
+
q,
|
894
|
+
online_results,
|
895
|
+
meta_log,
|
896
|
+
model=conversation_config.chat_model,
|
897
|
+
api_key=api_key,
|
898
|
+
completion_func=partial_completion,
|
899
|
+
conversation_commands=conversation_commands,
|
900
|
+
max_prompt_size=conversation_config.max_prompt_size,
|
901
|
+
tokenizer_name=conversation_config.tokenizer,
|
902
|
+
location_data=location_data,
|
903
|
+
user_name=user_name,
|
904
|
+
agent=agent,
|
905
|
+
)
|
854
906
|
|
855
907
|
metadata.update({"chat_model": conversation_config.chat_model})
|
856
908
|
|
@@ -861,129 +913,6 @@ def generate_chat_response(
|
|
861
913
|
return chat_response, metadata
|
862
914
|
|
863
915
|
|
864
|
-
async def text_to_image(
|
865
|
-
message: str,
|
866
|
-
user: KhojUser,
|
867
|
-
conversation_log: dict,
|
868
|
-
location_data: LocationData,
|
869
|
-
references: List[Dict[str, Any]],
|
870
|
-
online_results: Dict[str, Any],
|
871
|
-
subscribed: bool = False,
|
872
|
-
send_status_func: Optional[Callable] = None,
|
873
|
-
uploaded_image_url: Optional[str] = None,
|
874
|
-
):
|
875
|
-
status_code = 200
|
876
|
-
image = None
|
877
|
-
response = None
|
878
|
-
image_url = None
|
879
|
-
intent_type = ImageIntentType.TEXT_TO_IMAGE_V3
|
880
|
-
|
881
|
-
text_to_image_config = await ConversationAdapters.aget_user_text_to_image_model(user)
|
882
|
-
if not text_to_image_config:
|
883
|
-
# If the user has not configured a text to image model, return an unsupported on server error
|
884
|
-
status_code = 501
|
885
|
-
message = "Failed to generate image. Setup image generation on the server."
|
886
|
-
yield image_url or image, status_code, message, intent_type.value
|
887
|
-
return
|
888
|
-
|
889
|
-
text2image_model = text_to_image_config.model_name
|
890
|
-
chat_history = ""
|
891
|
-
for chat in conversation_log.get("chat", [])[-4:]:
|
892
|
-
if chat["by"] == "khoj" and chat["intent"].get("type") in ["remember", "reminder"]:
|
893
|
-
chat_history += f"Q: {chat['intent']['query']}\n"
|
894
|
-
chat_history += f"A: {chat['message']}\n"
|
895
|
-
elif chat["by"] == "khoj" and "text-to-image" in chat["intent"].get("type"):
|
896
|
-
chat_history += f"Q: Prompt: {chat['intent']['query']}\n"
|
897
|
-
chat_history += f"A: Improved Prompt: {chat['intent']['inferred-queries'][0]}\n"
|
898
|
-
|
899
|
-
if send_status_func:
|
900
|
-
async for event in send_status_func("**Enhancing the Painting Prompt**"):
|
901
|
-
yield {ChatEvent.STATUS: event}
|
902
|
-
improved_image_prompt = await generate_better_image_prompt(
|
903
|
-
message,
|
904
|
-
chat_history,
|
905
|
-
location_data=location_data,
|
906
|
-
note_references=references,
|
907
|
-
online_results=online_results,
|
908
|
-
model_type=text_to_image_config.model_type,
|
909
|
-
subscribed=subscribed,
|
910
|
-
uploaded_image_url=uploaded_image_url,
|
911
|
-
)
|
912
|
-
|
913
|
-
if send_status_func:
|
914
|
-
async for event in send_status_func(f"**Painting to Imagine**:\n{improved_image_prompt}"):
|
915
|
-
yield {ChatEvent.STATUS: event}
|
916
|
-
|
917
|
-
if text_to_image_config.model_type == TextToImageModelConfig.ModelType.OPENAI:
|
918
|
-
with timer("Generate image with OpenAI", logger):
|
919
|
-
if text_to_image_config.api_key:
|
920
|
-
api_key = text_to_image_config.api_key
|
921
|
-
elif text_to_image_config.openai_config:
|
922
|
-
api_key = text_to_image_config.openai_config.api_key
|
923
|
-
elif state.openai_client:
|
924
|
-
api_key = state.openai_client.api_key
|
925
|
-
auth_header = {"Authorization": f"Bearer {api_key}"} if api_key else {}
|
926
|
-
try:
|
927
|
-
response = state.openai_client.images.generate(
|
928
|
-
prompt=improved_image_prompt,
|
929
|
-
model=text2image_model,
|
930
|
-
response_format="b64_json",
|
931
|
-
extra_headers=auth_header,
|
932
|
-
)
|
933
|
-
image = response.data[0].b64_json
|
934
|
-
decoded_image = base64.b64decode(image)
|
935
|
-
except openai.OpenAIError or openai.BadRequestError or openai.APIConnectionError as e:
|
936
|
-
if "content_policy_violation" in e.message:
|
937
|
-
logger.error(f"Image Generation blocked by OpenAI: {e}")
|
938
|
-
status_code = e.status_code # type: ignore
|
939
|
-
message = f"Image generation blocked by OpenAI: {e.message}" # type: ignore
|
940
|
-
yield image_url or image, status_code, message, intent_type.value
|
941
|
-
return
|
942
|
-
else:
|
943
|
-
logger.error(f"Image Generation failed with {e}", exc_info=True)
|
944
|
-
message = f"Image generation failed with OpenAI error: {e.message}" # type: ignore
|
945
|
-
status_code = e.status_code # type: ignore
|
946
|
-
yield image_url or image, status_code, message, intent_type.value
|
947
|
-
return
|
948
|
-
|
949
|
-
elif text_to_image_config.model_type == TextToImageModelConfig.ModelType.STABILITYAI:
|
950
|
-
with timer("Generate image with Stability AI", logger):
|
951
|
-
try:
|
952
|
-
response = requests.post(
|
953
|
-
f"https://api.stability.ai/v2beta/stable-image/generate/sd3",
|
954
|
-
headers={"authorization": f"Bearer {text_to_image_config.api_key}", "accept": "image/*"},
|
955
|
-
files={"none": ""},
|
956
|
-
data={
|
957
|
-
"prompt": improved_image_prompt,
|
958
|
-
"model": text2image_model,
|
959
|
-
"mode": "text-to-image",
|
960
|
-
"output_format": "png",
|
961
|
-
"aspect_ratio": "1:1",
|
962
|
-
},
|
963
|
-
)
|
964
|
-
decoded_image = response.content
|
965
|
-
except requests.RequestException as e:
|
966
|
-
logger.error(f"Image Generation failed with {e}", exc_info=True)
|
967
|
-
message = f"Image generation failed with Stability AI error: {e}"
|
968
|
-
status_code = e.status_code # type: ignore
|
969
|
-
yield image_url or image, status_code, message, intent_type.value
|
970
|
-
return
|
971
|
-
|
972
|
-
with timer("Convert image to webp", logger):
|
973
|
-
# Convert png to webp for faster loading
|
974
|
-
webp_image_bytes = convert_image_to_webp(decoded_image)
|
975
|
-
|
976
|
-
with timer("Upload image to S3", logger):
|
977
|
-
image_url = upload_image(webp_image_bytes, user.uuid)
|
978
|
-
if image_url:
|
979
|
-
intent_type = ImageIntentType.TEXT_TO_IMAGE2
|
980
|
-
else:
|
981
|
-
intent_type = ImageIntentType.TEXT_TO_IMAGE_V3
|
982
|
-
image = base64.b64encode(webp_image_bytes).decode("utf-8")
|
983
|
-
|
984
|
-
yield image_url or image, status_code, improved_image_prompt, intent_type.value
|
985
|
-
|
986
|
-
|
987
916
|
class ApiUserRateLimiter:
|
988
917
|
def __init__(self, requests: int, subscribed_requests: int, window: int, slug: str):
|
989
918
|
self.requests = requests
|
@@ -1217,21 +1146,14 @@ def scheduled_chat(
|
|
1217
1146
|
token = token[0].token
|
1218
1147
|
headers["Authorization"] = f"Bearer {token}"
|
1219
1148
|
|
1220
|
-
# Log request details
|
1221
|
-
logger.info(f"POST URL: {url}")
|
1222
|
-
logger.info(f"Headers: {headers}")
|
1223
|
-
logger.info(f"Payload: {json_payload}")
|
1224
|
-
|
1225
1149
|
# Call the chat API endpoint with authenticated user token and query
|
1226
|
-
raw_response = requests.post(url, headers=headers, json=json_payload)
|
1227
|
-
|
1228
|
-
#
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
for resp in raw_response.history:
|
1234
|
-
logger.info(f"Redirected from {resp.url} with status code {resp.status_code}")
|
1150
|
+
raw_response = requests.post(url, headers=headers, json=json_payload, allow_redirects=False)
|
1151
|
+
|
1152
|
+
# Handle redirect manually if necessary
|
1153
|
+
if raw_response.status_code in [301, 302]:
|
1154
|
+
redirect_url = raw_response.headers["Location"]
|
1155
|
+
logger.info(f"Redirecting to {redirect_url}")
|
1156
|
+
raw_response = requests.post(redirect_url, headers=headers, json=json_payload)
|
1235
1157
|
|
1236
1158
|
# Stop if the chat API call was not successful
|
1237
1159
|
if raw_response.status_code != 200:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: khoj
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.23.3.dev1
|
4
4
|
Summary: Your Second Brain
|
5
5
|
Project-URL: Homepage, https://khoj.dev
|
6
6
|
Project-URL: Documentation, https://docs.khoj.dev
|
@@ -36,6 +36,7 @@ Requires-Dist: django==5.0.8
|
|
36
36
|
Requires-Dist: docx2txt==0.8
|
37
37
|
Requires-Dist: einops==0.8.0
|
38
38
|
Requires-Dist: fastapi>=0.110.0
|
39
|
+
Requires-Dist: google-generativeai==0.7.2
|
39
40
|
Requires-Dist: httpx==0.25.0
|
40
41
|
Requires-Dist: huggingface-hub>=0.22.2
|
41
42
|
Requires-Dist: itsdangerous==2.1.2
|