mirror of
https://github.com/xtekky/gpt4free.git
synced 2026-04-22 23:57:17 +08:00
Fix upload files in demo mode
Updates for memory with mem0 Fix asyncio import in nodriver function Add provider specific api endpoints Support for open settings in UI at /chat/settings
This commit is contained in:
@@ -245,6 +245,25 @@ class Api:
|
||||
]
|
||||
}
|
||||
|
||||
@self.app.get("/{provider}/models", responses={
|
||||
HTTP_200_OK: {"model": List[ModelResponseModel]},
|
||||
})
|
||||
async def models(provider: str):
|
||||
if provider not in ProviderUtils.convert:
|
||||
return ErrorResponse.from_message("The provider does not exist.", 404)
|
||||
provider: ProviderType = ProviderUtils.convert[provider]
|
||||
return {
|
||||
"object": "list",
|
||||
"data": [{
|
||||
"id": model,
|
||||
"object": "model",
|
||||
"created": 0,
|
||||
"owned_by": getattr(provider, "label", provider.__name__),
|
||||
"image": model in getattr(provider, "image_models", []),
|
||||
"image": model in getattr(provider, "vision_models", []),
|
||||
} for model in provider.get_models() if hasattr(provider, "get_models")]
|
||||
}
|
||||
|
||||
@self.app.get("/v1/models/{model_name}", responses={
|
||||
HTTP_200_OK: {"model": ModelResponseModel},
|
||||
HTTP_404_NOT_FOUND: {"model": ErrorResponseModel},
|
||||
@@ -352,6 +371,20 @@ class Api:
|
||||
logger.exception(e)
|
||||
return ErrorResponse.from_exception(e, config, HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
@self.app.post("/{provider}/chat/completions", responses={
|
||||
HTTP_200_OK: {"model": ChatCompletion},
|
||||
HTTP_401_UNAUTHORIZED: {"model": ErrorResponseModel},
|
||||
HTTP_404_NOT_FOUND: {"model": ErrorResponseModel},
|
||||
HTTP_422_UNPROCESSABLE_ENTITY: {"model": ErrorResponseModel},
|
||||
HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponseModel},
|
||||
})
|
||||
async def provider_chat_completions(
|
||||
provider: str,
|
||||
config: ChatCompletionsConfig,
|
||||
credentials: Annotated[HTTPAuthorizationCredentials, Depends(Api.security)] = None,
|
||||
):
|
||||
return await chat_completions(config, credentials, provider)
|
||||
|
||||
responses = {
|
||||
HTTP_200_OK: {"model": ImagesResponse},
|
||||
HTTP_401_UNAUTHORIZED: {"model": ErrorResponseModel},
|
||||
|
||||
@@ -162,6 +162,19 @@
|
||||
document.getElementById('recognition-language').placeholder = navigator.language;
|
||||
</script>
|
||||
</div>
|
||||
<div class="field mem0 hidden">
|
||||
<span class="label">Enable Memory with Mem0</span>
|
||||
<input type="checkbox" id="mem0"/>
|
||||
<label for="mem0" class="toogle" title=""></label>
|
||||
<button onclick="import_memory()">
|
||||
<i class="fa-solid fa-arrow-up-from-bracket"></i>
|
||||
<span>Import Messages to Mem0</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="field box hidden">
|
||||
<label for="mem0-api_key" class="label" title="">Mem0 API:</label>
|
||||
<input type="text" id="mem0-api_key" name="mem0[api_key]" placeholder="api_key"/>
|
||||
</div>
|
||||
<div class="field box">
|
||||
<label for="Custom-api_base" class="label" title="">Custom Provider (Base Url):</label>
|
||||
<input type="text" id="Custom-api_base" name="Custom[api_base]" placeholder="http://localhost:8080/v1"/>
|
||||
@@ -189,12 +202,6 @@
|
||||
<a href="" onclick="return false;">Show log</a>
|
||||
</button>
|
||||
</div>
|
||||
<div class="bottom_buttons memory hidden">
|
||||
<button onclick="import_memory()">
|
||||
<i class="fa-solid fa-arrow-up-from-bracket"></i>
|
||||
<a href="" onclick="return false;">Import Messages to Memory</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="provider_forms hidden">
|
||||
<div class="bottom_buttons">
|
||||
|
||||
@@ -221,12 +221,12 @@ body:not(.white) a:visited{
|
||||
background-color: var(--blur-bg);
|
||||
}
|
||||
|
||||
.conversations i, .bottom_buttons i {
|
||||
.conversations i, .bottom_buttons i, .mem0 button i {
|
||||
color: var(--conversations);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bottom_buttons i {
|
||||
.bottom_buttons i, .mem0 button i {
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
@@ -998,7 +998,7 @@ select:hover,
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.bottom_buttons button {
|
||||
.bottom_buttons button, .mem0 button {
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
@@ -1011,10 +1011,15 @@ select:hover,
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mem0 button {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.bottom_buttons button a,
|
||||
.bottom_buttons button span,
|
||||
.bottom_buttons .info a,
|
||||
.bottom_buttons .info i {
|
||||
.bottom_buttons .info i,
|
||||
.mem0 button span {
|
||||
color: var(--colour-3);
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -1129,7 +1134,6 @@ ul {
|
||||
padding-left: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 990px) {
|
||||
.conversations {
|
||||
display: none;
|
||||
|
||||
@@ -1533,7 +1533,7 @@ async function hide_sidebar() {
|
||||
chat.classList.remove("hidden");
|
||||
log_storage.classList.add("hidden");
|
||||
await hide_settings();
|
||||
if (window.location.pathname == "/menu/" || window.location.pathname == "/settings/") {
|
||||
if (window.location.pathname.endsWith("/menu/") || window.location.pathname.endsWith("/settings/")) {
|
||||
history.back();
|
||||
}
|
||||
}
|
||||
@@ -1550,10 +1550,7 @@ sidebar_button.addEventListener("click", async () => {
|
||||
if (sidebar.classList.contains("shown")) {
|
||||
await hide_sidebar();
|
||||
} else {
|
||||
sidebar.classList.add("shown");
|
||||
sidebar_button.classList.add("rotated");
|
||||
await hide_settings();
|
||||
add_url_to_history("/menu/");
|
||||
await show_menu();
|
||||
}
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
@@ -1564,12 +1561,19 @@ function add_url_to_history(url) {
|
||||
}
|
||||
}
|
||||
|
||||
async function show_menu() {
|
||||
sidebar.classList.add("shown");
|
||||
sidebar_button.classList.add("rotated");
|
||||
await hide_settings();
|
||||
add_url_to_history("/chat/menu/");
|
||||
}
|
||||
|
||||
function open_settings() {
|
||||
if (settings.classList.contains("hidden")) {
|
||||
chat.classList.add("hidden");
|
||||
sidebar.classList.remove("shown");
|
||||
settings.classList.remove("hidden");
|
||||
add_url_to_history("/settings/");
|
||||
add_url_to_history("/chat/settings/");
|
||||
} else {
|
||||
settings.classList.add("hidden");
|
||||
chat.classList.remove("hidden");
|
||||
@@ -1782,7 +1786,9 @@ window.addEventListener('pywebviewready', async function() {
|
||||
|
||||
async function on_load() {
|
||||
count_input();
|
||||
if (/\/chat\/[^?]+/.test(window.location.href)) {
|
||||
if (/\/settings\//.test(window.location.href)) {
|
||||
open_settings();
|
||||
} else if (/\/chat\/[^?]+/.test(window.location.href)) {
|
||||
load_conversation(window.conversation_id);
|
||||
} else {
|
||||
chatPrompt.value = document.getElementById("systemPrompt")?.value || "";
|
||||
@@ -1878,7 +1884,7 @@ async function on_api() {
|
||||
}
|
||||
providerSelect.innerHTML = '<option value="" selected>Demo Mode</option>'
|
||||
document.getElementById("pin").disabled = true;
|
||||
document.getElementById("refine")?.parentElement.remove();
|
||||
document.getElementById("refine")?.parentElement.classList.add("hidden")
|
||||
const track_usage = document.getElementById("track_usage");
|
||||
track_usage.checked = true;
|
||||
track_usage.disabled = true;
|
||||
@@ -2099,7 +2105,7 @@ async function upload_files(fileInput) {
|
||||
body: formData
|
||||
});
|
||||
|
||||
let do_refine = document.getElementById("refine").checked;
|
||||
let do_refine = document.getElementById("refine")?.checked;
|
||||
function connectToSSE(url) {
|
||||
const eventSource = new EventSource(url);
|
||||
eventSource.onmessage = (event) => {
|
||||
@@ -2417,7 +2423,18 @@ function save_storage() {
|
||||
}
|
||||
|
||||
function import_memory() {
|
||||
if (!appStorage.getItem("mem0-api_key")) {
|
||||
return;
|
||||
}
|
||||
hide_sidebar();
|
||||
|
||||
let count = 0;
|
||||
let user_id = appStorage.getItem("user") || appStorage.getItem("mem0-user_id");
|
||||
if (!user_id) {
|
||||
user_id = uuid();
|
||||
appStorage.setItem("mem0-user_id", user_id);
|
||||
}
|
||||
inputCount.innerText = `Start importing to Mem0...`;
|
||||
let conversations = [];
|
||||
for (let i = 0; i < appStorage.length; i++) {
|
||||
if (appStorage.key(i).startsWith("conversation:")) {
|
||||
@@ -2426,17 +2443,21 @@ function import_memory() {
|
||||
}
|
||||
}
|
||||
conversations.sort((a, b) => (a.updated||0)-(b.updated||0));
|
||||
let count = 0;
|
||||
conversations.forEach(async (conversation)=>{
|
||||
let body = JSON.stringify(conversation);
|
||||
response = await fetch("/backend-api/v2/memory", {
|
||||
method: 'POST',
|
||||
body: body,
|
||||
headers: {"content-type": "application/json"}
|
||||
});
|
||||
const result = await response.json();
|
||||
count += result.count;
|
||||
inputCount.innerText = `${count} Messages are imported`;
|
||||
conversations.forEach(async (conversation, i)=>{
|
||||
setTimeout(async ()=>{
|
||||
let body = JSON.stringify(conversation);
|
||||
response = await fetch(`/backend-api/v2/memory/${user_id}`, {
|
||||
method: 'POST',
|
||||
body: body,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x_api_key": appStorage.getItem("mem0-api_key")
|
||||
}
|
||||
});
|
||||
const result = await response.json();
|
||||
count += result.count;
|
||||
inputCount.innerText = `${count} Messages were imported`;
|
||||
}, (i+1)*1000);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class Api:
|
||||
if provider in ProviderUtils.convert:
|
||||
provider = ProviderUtils.convert[provider]
|
||||
if issubclass(provider, ProviderModelMixin):
|
||||
if api_key is not None and "api_key" in signature(provider.get_models).parameters:
|
||||
if "api_key" in signature(provider.get_models).parameters:
|
||||
models = provider.get_models(api_key=api_key, api_base=api_base)
|
||||
else:
|
||||
models = provider.get_models()
|
||||
|
||||
@@ -171,16 +171,16 @@ class Backend_Api(Api):
|
||||
f.write(f"{json.dumps(request.json)}\n")
|
||||
return {}
|
||||
|
||||
@app.route('/backend-api/v2/memory', methods=['POST'])
|
||||
def add_memory():
|
||||
@app.route('/backend-api/v2/memory/<user_id>', methods=['POST'])
|
||||
def add_memory(user_id: str):
|
||||
api_key = request.headers.get("x_api_key")
|
||||
json_data = request.json
|
||||
from mem0 import MemoryClient
|
||||
client = MemoryClient(api_key=api_key)
|
||||
client.add(
|
||||
[{"role": item["role"], "content": item["content"]} for item in json_data.get("items")],
|
||||
user_id="user",
|
||||
metadata={"conversation_id": json_data.get("id"), "title": json_data.get("title")}
|
||||
user_id=user_id,
|
||||
metadata={"conversation_id": json_data.get("id")}
|
||||
)
|
||||
return {"count": len(json_data.get("items"))}
|
||||
|
||||
@@ -189,13 +189,19 @@ class Backend_Api(Api):
|
||||
api_key = request.headers.get("x_api_key")
|
||||
from mem0 import MemoryClient
|
||||
client = MemoryClient(api_key=api_key)
|
||||
if request.args.search:
|
||||
if request.args.get("search"):
|
||||
return client.search(
|
||||
request.args.search,
|
||||
request.args.get("search"),
|
||||
user_id=user_id,
|
||||
metadata=json.loads(request.args.metadata) if request.args.metadata else None
|
||||
filters=json.loads(request.args.get("filters", "null")),
|
||||
metadata=json.loads(request.args.get("metadata", "null"))
|
||||
)
|
||||
return {}
|
||||
return client.get_all(
|
||||
user_id=user_id,
|
||||
page=request.args.get("page", 1),
|
||||
page_size=request.args.get("page_size", 100),
|
||||
filters=json.loads(request.args.get("filters", "null")),
|
||||
)
|
||||
|
||||
self.routes = {
|
||||
'/backend-api/v2/version': {
|
||||
|
||||
@@ -16,12 +16,12 @@ class Website:
|
||||
'function': self._chat,
|
||||
'methods': ['GET', 'POST']
|
||||
},
|
||||
'/menu/': {
|
||||
'/chat/menu/': {
|
||||
'function': redirect_home,
|
||||
'methods': ['GET', 'POST']
|
||||
},
|
||||
'/settings/': {
|
||||
'function': redirect_home,
|
||||
'/chat/settings/': {
|
||||
'function': self._settings,
|
||||
'methods': ['GET', 'POST']
|
||||
},
|
||||
'/images/': {
|
||||
@@ -36,4 +36,7 @@ class Website:
|
||||
return render_template('index.html', chat_id=conversation_id)
|
||||
|
||||
def _index(self):
|
||||
return render_template('index.html', chat_id=str(uuid.uuid4()))
|
||||
|
||||
def _settings(self):
|
||||
return render_template('index.html', chat_id=str(uuid.uuid4()))
|
||||
@@ -7,6 +7,7 @@ from urllib.parse import urlparse
|
||||
from typing import Iterator
|
||||
from http.cookies import Morsel
|
||||
from pathlib import Path
|
||||
import asyncio
|
||||
try:
|
||||
from curl_cffi.requests import Session, Response
|
||||
from .curl_cffi import StreamResponse, StreamSession, FormData
|
||||
@@ -17,7 +18,6 @@ except ImportError:
|
||||
has_curl_cffi = False
|
||||
try:
|
||||
import webview
|
||||
import asyncio
|
||||
has_webview = True
|
||||
except ImportError:
|
||||
has_webview = False
|
||||
|
||||
Reference in New Issue
Block a user