Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenDAS
ktransformers
Commits
18c42e67
Commit
18c42e67
authored
Jul 27, 2024
by
chenxl
Browse files
Initial commit
parents
Changes
247
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
1035 additions
and
0 deletions
+1035
-0
ktransformers/server/crud/__init__.py
ktransformers/server/crud/__init__.py
+0
-0
ktransformers/server/crud/assistants/__init__.py
ktransformers/server/crud/assistants/__init__.py
+0
-0
ktransformers/server/crud/assistants/assistants.py
ktransformers/server/crud/assistants/assistants.py
+66
-0
ktransformers/server/crud/assistants/messages.py
ktransformers/server/crud/assistants/messages.py
+86
-0
ktransformers/server/crud/assistants/runs.py
ktransformers/server/crud/assistants/runs.py
+50
-0
ktransformers/server/crud/assistants/threads.py
ktransformers/server/crud/assistants/threads.py
+93
-0
ktransformers/server/exceptions.py
ktransformers/server/exceptions.py
+23
-0
ktransformers/server/main.py
ktransformers/server/main.py
+143
-0
ktransformers/server/models/__init__.py
ktransformers/server/models/__init__.py
+0
-0
ktransformers/server/models/assistants/__init__.py
ktransformers/server/models/assistants/__init__.py
+0
-0
ktransformers/server/models/assistants/assistants.py
ktransformers/server/models/assistants/assistants.py
+29
-0
ktransformers/server/models/assistants/messages.py
ktransformers/server/models/assistants/messages.py
+28
-0
ktransformers/server/models/assistants/run_steps.py
ktransformers/server/models/assistants/run_steps.py
+31
-0
ktransformers/server/models/assistants/runs.py
ktransformers/server/models/assistants/runs.py
+39
-0
ktransformers/server/models/assistants/threads.py
ktransformers/server/models/assistants/threads.py
+18
-0
ktransformers/server/requirements.txt
ktransformers/server/requirements.txt
+14
-0
ktransformers/server/schemas/__init__.py
ktransformers/server/schemas/__init__.py
+0
-0
ktransformers/server/schemas/assistants/__init__.py
ktransformers/server/schemas/assistants/__init__.py
+0
-0
ktransformers/server/schemas/assistants/assistants.py
ktransformers/server/schemas/assistants/assistants.py
+202
-0
ktransformers/server/schemas/assistants/messages.py
ktransformers/server/schemas/assistants/messages.py
+213
-0
No files found.
ktransformers/server/crud/__init__.py
0 → 100644
View file @
18c42e67
ktransformers/server/crud/assistants/__init__.py
0 → 100644
View file @
18c42e67
ktransformers/server/crud/assistants/assistants.py
0 → 100644
View file @
18c42e67
from
time
import
time
from
typing
import
Optional
,
List
from
uuid
import
uuid4
from
ktransformers.server.models.assistants.assistants
import
Assistant
from
ktransformers.server.schemas.assistants.assistants
import
AssistantCreate
,
AssistantObject
,
AssistantModify
from
ktransformers.server.utils.sql_utils
import
SQLUtil
from
ktransformers.server.config.log
import
logger
from
ktransformers.server.schemas.base
import
Order
class
AssistantDatabaseManager
:
def
__init__
(
self
)
->
None
:
self
.
sql_util
=
SQLUtil
()
def
create_assistant_object
(
self
,
assistant
:
AssistantCreate
)
->
AssistantObject
:
assistant
=
AssistantObject
(
**
assistant
.
model_dump
(
mode
=
'json'
),
id
=
str
(
uuid4
()),
object
=
'assistant'
,
created_at
=
int
(
time
()),
)
return
assistant
def
db_count_assistants
(
self
)
->
int
:
with
self
.
sql_util
.
get_db
()
as
db
:
return
db
.
query
(
Assistant
).
count
()
def
db_create_assistant
(
self
,
assistant
:
AssistantCreate
):
ass_obj
=
self
.
create_assistant_object
(
assistant
)
ass_obj
.
sync_db
()
return
ass_obj
def
db_list_assistants
(
self
,
limit
:
Optional
[
int
],
order
:
Order
)
->
List
[
AssistantObject
]:
with
self
.
sql_util
.
get_db
()
as
db
:
query
=
db
.
query
(
Assistant
).
order_by
(
order
.
to_sqlalchemy_order
()(
Assistant
.
created_at
))
if
limit
is
not
None
:
db_assistants
=
query
.
limit
(
limit
)
else
:
db_assistants
=
query
.
all
()
return
[
AssistantObject
.
model_validate
(
a
.
__dict__
)
for
a
in
db_assistants
]
def
db_get_assistant_by_id
(
self
,
assistant_id
:
str
)
->
Optional
[
AssistantObject
]:
with
self
.
sql_util
.
get_db
()
as
db
:
db_assistant
=
db
.
query
(
Assistant
).
filter
(
Assistant
.
id
==
assistant_id
).
first
()
if
db_assistant
is
None
:
logger
.
debug
(
f
"no assistant with id
{
str
}
"
)
return
None
return
AssistantObject
.
model_validate
(
db_assistant
.
__dict__
)
def
db_update_assistant_by_id
(
self
,
assistant_id
:
str
,
assistant
:
AssistantModify
):
with
self
.
sql_util
.
get_db
()
as
db
:
db_assistant
=
db
.
query
(
Assistant
).
filter
(
Assistant
.
id
==
assistant_id
).
first
()
self
.
sql_util
.
db_update_commit_refresh
(
db
,
db_assistant
,
assistant
)
return
AssistantObject
.
model_validate
(
db_assistant
.
__dict__
)
def
db_delete_assistant_by_id
(
self
,
assistant_id
:
str
):
with
self
.
sql_util
.
get_db
()
as
db
:
db_assistant
=
db
.
query
(
Assistant
).
filter
(
Assistant
.
id
==
assistant_id
).
first
()
db
.
delete
(
db_assistant
)
db
.
commit
()
ktransformers/server/crud/assistants/messages.py
0 → 100644
View file @
18c42e67
from
time
import
time
from
typing
import
Optional
from
uuid
import
uuid4
from
ktransformers.server.models.assistants.messages
import
Message
from
ktransformers.server.schemas.assistants.messages
import
MessageCore
,
MessageCreate
,
MessageObject
from
ktransformers.server.schemas.base
import
Order
,
ObjectID
from
ktransformers.server.utils.sql_utils
import
SQLUtil
class
MessageDatabaseManager
:
def
__init__
(
self
)
->
None
:
self
.
sql_util
=
SQLUtil
()
@
staticmethod
def
create_db_message_by_core
(
message
:
MessageCore
):
message_dict
=
message
.
model_dump
(
mode
=
"json"
)
return
Message
(
**
message_dict
,
id
=
str
(
uuid4
()),
created_at
=
int
(
time
()))
def
create_db_message
(
self
,
message
:
MessageCreate
):
return
MessageDatabaseManager
.
create_db_message_by_core
(
message
.
to_core
())
def
db_add_message
(
self
,
message
:
Message
):
with
self
.
sql_util
.
get_db
()
as
db
:
db
.
add
(
message
)
self
.
sql_util
.
db_add_commit_refresh
(
db
,
message
)
def
db_create_message
(
self
,
thread_id
:
str
,
message
:
MessageCreate
,
status
:
MessageObject
.
Status
):
db_message
=
self
.
create_db_message
(
message
)
db_message
.
status
=
status
.
value
db_message
.
thread_id
=
thread_id
self
.
db_add_message
(
db_message
)
return
MessageObject
.
model_validate
(
db_message
.
__dict__
)
@
staticmethod
def
create_message_object
(
thread_id
:
ObjectID
,
run_id
:
ObjectID
,
message
:
MessageCreate
):
core
=
message
.
to_core
()
return
MessageObject
(
**
core
.
model_dump
(
mode
=
'json'
),
id
=
str
(
uuid4
()),
object
=
'thread.message'
,
created_at
=
int
(
time
()),
thread_id
=
thread_id
,
run_id
=
run_id
,
status
=
MessageObject
.
Status
.
in_progress
,
)
def
db_sync_message
(
self
,
message
:
MessageObject
):
db_message
=
Message
(
**
message
.
model_dump
(
mode
=
"json"
),
)
with
self
.
sql_util
.
get_db
()
as
db
:
self
.
sql_util
.
db_merge_commit
(
db
,
db_message
)
def
db_list_messages_of_thread
(
self
,
thread_id
:
str
,
limit
:
Optional
[
int
]
=
None
,
order
:
Order
=
Order
.
DESC
):
# logger.debug(
# f"list messages of: {thread_id}, limit {limit}, order {order}")
with
self
.
sql_util
.
get_db
()
as
db
:
query
=
(
db
.
query
(
Message
)
.
filter
(
Message
.
thread_id
==
thread_id
)
.
order_by
(
order
.
to_sqlalchemy_order
()(
Message
.
created_at
))
)
if
limit
is
not
None
:
messages
=
query
.
limit
(
limit
)
else
:
messages
=
query
.
all
()
message_list
=
[
MessageObject
.
model_validate
(
m
.
__dict__
)
for
m
in
messages
]
return
message_list
def
db_get_message_by_id
(
self
,
thread_id
:
ObjectID
,
message_id
:
ObjectID
)
->
MessageObject
:
with
self
.
sql_util
.
get_db
()
as
db
:
message
=
db
.
query
(
Message
).
filter
(
Message
.
id
==
message_id
).
first
()
assert
message
.
thread_id
==
thread_id
message_info
=
MessageObject
.
model_validate
(
message
.
__dict__
)
return
message_info
def
db_delete_message_by_id
(
self
,
thread_id
:
ObjectID
,
message_id
:
ObjectID
):
with
self
.
sql_util
.
get_db
()
as
db
:
message
=
db
.
query
(
Message
).
filter
(
Message
.
id
==
message_id
).
first
()
assert
message
.
thread_id
==
thread_id
db
.
delete
(
message
)
db
.
commit
()
ktransformers/server/crud/assistants/runs.py
0 → 100644
View file @
18c42e67
from
time
import
time
from
uuid
import
uuid4
from
ktransformers.server.models.assistants.runs
import
Run
from
ktransformers.server.schemas.assistants.runs
import
RunCreate
,
RunObject
from
ktransformers.server.schemas.base
import
ObjectID
from
ktransformers.server.utils.sql_utils
import
SQLUtil
class
RunsDatabaseManager
:
def
__init__
(
self
)
->
None
:
self
.
sql_util
=
SQLUtil
()
def
create_run_object
(
self
,
thread_id
:
ObjectID
,
run
:
RunCreate
)
->
RunObject
:
run_obj
=
RunObject
(
**
run
.
model_dump
(
mode
=
'json'
,
exclude
=
{
"stream"
}),
id
=
str
(
uuid4
()),
object
=
'run'
,
created_at
=
int
(
time
()),
thread_id
=
thread_id
,
status
=
RunObject
.
Status
.
queued
,
)
run_obj
.
set_compute_save
(
0
)
return
run_obj
def
db_create_run
(
self
,
thread_id
:
str
,
run
:
RunCreate
):
db_run
=
Run
(
**
run
.
model_dump
(
mode
=
"json"
,
exclude
=
{
"stream"
}),
id
=
str
(
uuid4
()),
created_at
=
int
(
time
()),
status
=
"queued"
,
thread_id
=
thread_id
,
)
with
self
.
sql_util
.
get_db
()
as
db
:
self
.
sql_util
.
db_add_commit_refresh
(
db
,
db_run
)
run_obj
=
RunObject
.
model_validate
(
db_run
.
__dict__
)
run_obj
.
set_compute_save
(
0
)
return
run_obj
def
db_sync_run
(
self
,
run
:
RunObject
)
->
None
:
db_run
=
Run
(
**
run
.
model_dump
(
mode
=
'json'
),
)
with
self
.
sql_util
.
get_db
()
as
db
:
self
.
sql_util
.
db_merge_commit
(
db
,
db_run
)
def
db_get_run
(
self
,
run_id
:
ObjectID
)
->
RunObject
:
with
self
.
sql_util
.
get_db
()
as
db
:
db_run
=
db
.
query
(
Run
).
filter
(
Run
.
id
==
run_id
).
first
()
return
RunObject
.
model_validate
(
db_run
.
__dict__
)
ktransformers/server/crud/assistants/threads.py
0 → 100644
View file @
18c42e67
from
time
import
time
from
typing
import
Optional
,
List
from
uuid
import
uuid4
from
ktransformers.server.models.assistants.messages
import
Message
from
ktransformers.server.models.assistants.threads
import
Thread
from
ktransformers.server.schemas.assistants.threads
import
ThreadCreate
,
ThreadObject
from
ktransformers.server.schemas.base
import
ObjectID
,
Order
from
ktransformers.server.schemas.conversation
import
ThreadPreview
from
ktransformers.server.utils.sql_utils
import
SQLUtil
from
ktransformers.server.crud.assistants.messages
import
MessageDatabaseManager
from
ktransformers.server.config.log
import
logger
from
ktransformers.server.crud.assistants.assistants
import
AssistantDatabaseManager
class
ThreadsDatabaseManager
:
def
__init__
(
self
)
->
None
:
self
.
sql_util
=
SQLUtil
()
self
.
message_manager
=
MessageDatabaseManager
()
self
.
assistant_maanager
=
AssistantDatabaseManager
()
def
db_create_thread
(
self
,
thread
:
ThreadCreate
):
thread_id
=
str
(
uuid4
())
db_messages
=
[]
with
self
.
sql_util
.
get_db
()
as
db
:
if
thread
.
messages
is
not
None
:
logger
.
debug
(
"Creating messages first for thread"
)
for
message
in
thread
.
messages
:
db_message
:
Message
=
MessageDatabaseManager
.
create_db_message_by_core
(
message
)
db_message
.
role
=
"user"
db_message
.
thread_id
=
thread_id
db
.
add
(
db_message
)
db_messages
.
append
(
db_message
)
db_thread
=
Thread
(
**
thread
.
model_dump
(
exclude
=
"messages"
),
id
=
str
(
uuid4
()),
created_at
=
int
(
time
()),
messages
=
db_messages
,
)
self
.
sql_util
.
db_add_commit_refresh
(
db
,
db_thread
)
thread_obj
=
ThreadObject
.
model_validate
(
db_thread
.
__dict__
)
if
'assistant_id'
in
thread
.
meta_data
:
# assistant = self.assistant_maanager.db_get_assistant_by_id(thread.meta_data['assistant_id'], db)
assistant
=
self
.
assistant_maanager
.
db_get_assistant_by_id
(
thread
.
meta_data
[
'assistant_id'
])
logger
.
info
(
f
'Append this related thread to assistant
{
assistant
.
id
}
'
)
assistant
.
append_related_threads
([
thread_obj
.
id
])
assistant
.
sync_db
(
db
)
return
thread_obj
def
db_get_thread_by_id
(
self
,
thread_id
:
ObjectID
):
with
self
.
sql_util
.
get_db
()
as
db
:
db_thread
=
db
.
query
(
Thread
).
filter
(
Thread
.
id
==
thread_id
).
first
()
return
ThreadObject
.
model_validate
(
db_thread
.
__dict__
)
def
db_list_threads
(
self
,
limit
:
Optional
[
int
],
order
:
Order
)
->
List
[
ThreadObject
]:
with
self
.
sql_util
.
get_db
()
as
db
:
query
=
db
.
query
(
Thread
).
order_by
(
order
.
to_sqlalchemy_order
()(
Thread
.
created_at
)).
filter
(
~
Thread
.
meta_data
.
contains
(
'assistant_id'
))
if
limit
is
not
None
:
db_threads
=
query
.
limit
(
limit
)
else
:
db_threads
=
query
.
all
()
return
[
ThreadObject
.
model_validate
(
tool
.
__dict__
)
for
tool
in
db_threads
]
def
db_list_threads_preview
(
self
,
limit
:
Optional
[
int
],
order
:
Order
)
->
List
[
ThreadPreview
]:
threads
=
self
.
db_list_threads
(
limit
,
order
)
previews
=
[]
for
thread
in
threads
:
messages
=
self
.
message_manager
.
db_list_messages_of_thread
(
thread
.
id
,
limit
=
2
,
order
=
Order
.
ASC
)
if
len
(
messages
)
==
2
:
message
=
messages
[
0
]
assistant
=
self
.
assistant_maanager
.
db_get_assistant_by_id
(
messages
[
1
].
assistant_id
)
else
:
message
=
None
assistant
=
None
previews
.
append
(
ThreadPreview
(
assistant
=
assistant
,
thread
=
thread
,
first_message
=
message
))
return
previews
def
db_delete_thread_by_id
(
self
,
thread_id
:
ObjectID
):
with
self
.
sql_util
.
get_db
()
as
db
:
db_thread
=
db
.
query
(
Thread
).
filter
(
Thread
.
id
==
thread_id
).
first
()
db
.
delete
(
db_thread
)
# TODO delete related messages and runs and other stuff or just gc
db
.
commit
()
ktransformers/server/exceptions.py
0 → 100644
View file @
18c42e67
from
fastapi
import
HTTPException
,
status
def
db_exception
():
return
HTTPException
(
status_code
=
status
.
HTTP_500_INTERNAL_SERVER_ERROR
,
detail
=
"DB Error"
,
)
def
not_implemented
(
what
):
return
HTTPException
(
status_code
=
status
.
HTTP_501_NOT_IMPLEMENTED
,
detail
=
f
"
{
what
}
not implemented"
,
)
def
internal_server_error
(
what
):
return
HTTPException
(
status_code
=
status
.
HTTP_500_INTERNAL_SERVER_ERROR
,
detail
=
f
"
{
what
}
"
)
def
request_error
(
what
):
return
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
f
"
{
what
}
"
)
ktransformers/server/main.py
0 → 100644
View file @
18c42e67
import
os
import
re
from
fastapi
import
FastAPI
from
fastapi.staticfiles
import
StaticFiles
import
uvicorn.logging
import
argparse
import
uvicorn
from
fastapi.middleware.cors
import
CORSMiddleware
from
ktransformers.server.config.config
import
Config
from
ktransformers.server.utils.create_interface
import
create_interface
from
ktransformers.server.backend.args
import
default_args
from
fastapi.openapi.utils
import
get_openapi
from
fastapi
import
FastAPI
from
fastapi.middleware.cors
import
CORSMiddleware
from
ktransformers.server.api
import
router
,
post_db_creation_operations
from
ktransformers.server.utils.sql_utils
import
Base
,
SQLUtil
from
ktransformers.server.config.log
import
logger
def
mount_app_routes
(
mount_app
:
FastAPI
):
sql_util
=
SQLUtil
()
logger
.
info
(
"Creating SQL tables"
)
Base
.
metadata
.
create_all
(
bind
=
sql_util
.
sqlalchemy_engine
)
post_db_creation_operations
()
mount_app
.
include_router
(
router
)
def
create_app
():
cfg
=
Config
()
app
=
FastAPI
()
if
Config
().
web_cross_domain
:
app
.
add_middleware
(
CORSMiddleware
,
allow_origins
=
[
"*"
],
allow_credentials
=
True
,
allow_methods
=
[
"*"
],
allow_headers
=
[
"*"
],
)
mount_app_routes
(
app
)
if
cfg
.
mount_web
:
mount_index_routes
(
app
)
return
app
def
update_web_port
(
config_file
:
str
):
ip_port_pattern
=
r
"(localhost|((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)):[0-9]{1,5}"
with
open
(
config_file
,
"r"
,
encoding
=
"utf-8"
)
as
f_cfg
:
web_config
=
f_cfg
.
read
()
ip_port
=
"localhost:"
+
str
(
Config
().
server_port
)
new_web_config
=
re
.
sub
(
ip_port_pattern
,
ip_port
,
web_config
)
with
open
(
config_file
,
"w"
,
encoding
=
"utf-8"
)
as
f_cfg
:
f_cfg
.
write
(
new_web_config
)
def
mount_index_routes
(
app
:
FastAPI
):
project_dir
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
))
web_dir
=
os
.
path
.
join
(
project_dir
,
"website/dist"
)
web_config_file
=
os
.
path
.
join
(
web_dir
,
"config.js"
)
update_web_port
(
web_config_file
)
if
os
.
path
.
exists
(
web_dir
):
app
.
mount
(
"/web"
,
StaticFiles
(
directory
=
web_dir
),
name
=
"static"
)
else
:
err_str
=
f
"No website resources in
{
web_dir
}
, please complile the website by npm first"
logger
.
error
(
err_str
)
print
(
err_str
)
exit
(
1
)
def
run_api
(
app
,
host
,
port
,
**
kwargs
):
if
kwargs
.
get
(
"ssl_keyfile"
)
and
kwargs
.
get
(
"ssl_certfile"
):
uvicorn
.
run
(
app
,
host
=
host
,
port
=
port
,
ssl_keyfile
=
kwargs
.
get
(
"ssl_keyfile"
),
ssl_certfile
=
kwargs
.
get
(
"ssl_certfile"
),
)
else
:
uvicorn
.
run
(
app
,
host
=
host
,
port
=
port
,
log_level
=
'debug'
)
def
custom_openapi
(
app
):
if
app
.
openapi_schema
:
return
app
.
openapi_schema
openapi_schema
=
get_openapi
(
title
=
"ktransformers server"
,
version
=
"1.0.0"
,
summary
=
"This is a server that provides a RESTful API for ktransformers."
,
description
=
"We provided chat completion and openai assistant interfaces."
,
routes
=
app
.
routes
,
)
openapi_schema
[
"info"
][
"x-logo"
]
=
{
"url"
:
"https://kvcache.ai/media/icon_1.png"
}
app
.
openapi_schema
=
openapi_schema
return
app
.
openapi_schema
def
main
():
cfg
=
Config
()
parser
=
argparse
.
ArgumentParser
(
prog
=
'kvcache.ai'
,
description
=
'Ktransformers'
)
parser
.
add_argument
(
"--host"
,
type
=
str
,
default
=
"0.0.0.0"
)
parser
.
add_argument
(
"--port"
,
type
=
int
,
default
=
cfg
.
server_port
)
parser
.
add_argument
(
"--ssl_keyfile"
,
type
=
str
)
parser
.
add_argument
(
"--ssl_certfile"
,
type
=
str
)
parser
.
add_argument
(
"--web"
,
type
=
bool
,
default
=
False
)
parser
.
add_argument
(
"--model_name"
,
type
=
str
,
default
=
cfg
.
model_name
)
parser
.
add_argument
(
"--model_path"
,
type
=
str
,
default
=
cfg
.
model_path
)
parser
.
add_argument
(
"--device"
,
type
=
str
,
default
=
cfg
.
model_device
)
parser
.
add_argument
(
"--gguf_path"
,
type
=
str
,
default
=
cfg
.
gguf_path
)
parser
.
add_argument
(
"--optimize_config_path"
,
type
=
str
,
required
=
False
)
parser
.
add_argument
(
"--cpu_infer"
,
type
=
int
,
default
=
cfg
.
cpu_infer
)
parser
.
add_argument
(
"--type"
,
type
=
str
,
default
=
cfg
.
backend_type
)
# 初始化消息
args
=
parser
.
parse_args
()
cfg
.
model_name
=
args
.
model_name
cfg
.
model_path
=
args
.
model_path
cfg
.
model_device
=
args
.
device
cfg
.
mount_web
=
args
.
web
cfg
.
server_ip
=
args
.
host
cfg
.
server_port
=
args
.
port
cfg
.
cpu_infer
=
args
.
cpu_infer
cfg
.
backend_type
=
args
.
type
default_args
.
model_dir
=
args
.
model_path
default_args
.
device
=
args
.
device
default_args
.
gguf_path
=
args
.
gguf_path
default_args
.
optimize_config_path
=
args
.
optimize_config_path
app
=
create_app
()
custom_openapi
(
app
)
create_interface
(
config
=
cfg
,
default_args
=
default_args
)
run_api
(
app
=
app
,
host
=
args
.
host
,
port
=
args
.
port
,
ssl_keyfile
=
args
.
ssl_keyfile
,
ssl_certfile
=
args
.
ssl_certfile
,)
if
__name__
==
"__main__"
:
main
()
ktransformers/server/models/__init__.py
0 → 100644
View file @
18c42e67
ktransformers/server/models/assistants/__init__.py
0 → 100644
View file @
18c42e67
ktransformers/server/models/assistants/assistants.py
0 → 100644
View file @
18c42e67
from
sqlalchemy
import
JSON
,
Column
,
Float
,
Integer
,
String
,
Text
from
sqlalchemy.orm
import
relationship
from
ktransformers.server.utils.sql_utils
import
Base
class
Assistant
(
Base
):
__tablename__
=
"assistants"
id
=
Column
(
String
,
primary_key
=
True
,
index
=
True
)
object
=
Column
(
String
,
default
=
"assistant"
)
created_at
=
Column
(
Integer
)
name
=
Column
(
String
,
nullable
=
True
)
description
=
Column
(
String
,
nullable
=
True
)
model
=
Column
(
String
)
instructions
=
Column
(
Text
,
nullable
=
True
)
tools
=
Column
(
JSON
)
tool_resources
=
Column
(
JSON
)
temperature
=
Column
(
Float
,
nullable
=
True
)
meta_data
=
Column
(
JSON
,
nullable
=
True
)
top_p
=
Column
(
Float
,
nullable
=
True
)
response_format
=
Column
(
JSON
,
default
=
"auto"
)
build_status
=
Column
(
JSON
,
nullable
=
True
)
runs
=
relationship
(
"Run"
,
back_populates
=
"assistant"
)
messages
=
relationship
(
"Message"
,
back_populates
=
"assistant"
)
ktransformers/server/models/assistants/messages.py
0 → 100644
View file @
18c42e67
from
sqlalchemy
import
JSON
,
Column
,
ForeignKey
,
Integer
,
String
from
sqlalchemy.orm
import
relationship
from
ktransformers.server.utils.sql_utils
import
Base
class
Message
(
Base
):
__tablename__
=
"messages"
id
=
Column
(
String
,
primary_key
=
True
,
index
=
True
)
object
=
Column
(
String
,
default
=
"thread.message"
)
created_at
=
Column
(
Integer
)
thread_id
=
Column
(
String
,
ForeignKey
(
"threads.id"
))
status
=
Column
(
String
,
default
=
"in_progress"
)
incomplete_details
=
Column
(
JSON
,
nullable
=
True
)
completed_at
=
Column
(
Integer
,
nullable
=
True
)
incomplete_at
=
Column
(
Integer
,
nullable
=
True
)
role
=
Column
(
JSON
)
content
=
Column
(
JSON
)
assistant_id
=
Column
(
String
,
ForeignKey
(
"assistants.id"
),
nullable
=
True
)
run_id
=
Column
(
String
,
ForeignKey
(
"runs.id"
),
nullable
=
True
)
attachments
=
Column
(
JSON
,
nullable
=
True
)
meta_data
=
Column
(
JSON
,
nullable
=
True
)
thread
=
relationship
(
"Thread"
,
back_populates
=
"messages"
)
assistant
=
relationship
(
"Assistant"
,
back_populates
=
"messages"
)
run
=
relationship
(
"Run"
,
back_populates
=
"message"
)
ktransformers/server/models/assistants/run_steps.py
0 → 100644
View file @
18c42e67
from
sqlalchemy
import
JSON
,
Column
,
ForeignKey
,
Integer
,
String
from
sqlalchemy.orm
import
relationship
from
ktransformers.server.utils.sql_utils
import
Base
class
RunStep
(
Base
):
__tablename__
=
"run_steps"
# todo
id
=
Column
(
String
,
primary_key
=
True
,
index
=
True
)
object
=
Column
(
String
,
default
=
"thread.run.step"
)
created_at
=
Column
(
Integer
)
assistant_id
=
Column
(
String
,
ForeignKey
(
"assistants.id"
))
thread_id
=
Column
(
String
,
ForeignKey
(
"threads.id"
))
run_id
=
Column
(
String
,
ForeignKey
(
"runs.id"
))
type
=
Column
(
String
)
status
=
Column
(
String
)
step_details
=
Column
(
JSON
)
last_error
=
Column
(
JSON
,
nullable
=
True
)
expires_at
=
Column
(
Integer
,
nullable
=
True
)
cancelled_at
=
Column
(
Integer
,
nullable
=
True
)
failed_at
=
Column
(
Integer
,
nullable
=
True
)
completed_at
=
Column
(
Integer
,
nullable
=
True
)
meta_data
=
Column
(
JSON
,
nullable
=
True
)
usage
=
Column
(
JSON
,
nullable
=
True
)
assistant
=
relationship
(
"Assistant"
,
back_populates
=
"run_steps"
)
thread
=
relationship
(
"Thread"
,
back_populates
=
"run_steps"
)
run
=
relationship
(
"Run"
,
back_populates
=
"run_steps"
)
ktransformers/server/models/assistants/runs.py
0 → 100644
View file @
18c42e67
from
sqlalchemy
import
JSON
,
Column
,
Float
,
ForeignKey
,
Integer
,
String
,
Text
from
sqlalchemy.orm
import
relationship
from
ktransformers.server.utils.sql_utils
import
Base
class
Run
(
Base
):
__tablename__
=
"runs"
id
=
Column
(
String
,
primary_key
=
True
,
index
=
True
)
object
=
Column
(
String
,
default
=
"thread.run"
)
created_at
=
Column
(
Integer
)
thread_id
=
Column
(
String
,
ForeignKey
(
"threads.id"
))
assistant_id
=
Column
(
String
,
ForeignKey
(
"assistants.id"
))
status
=
Column
(
String
)
required_action
=
Column
(
JSON
,
nullable
=
True
)
last_error
=
Column
(
JSON
,
nullable
=
True
)
expires_at
=
Column
(
Integer
,
nullable
=
True
)
started_at
=
Column
(
Integer
,
nullable
=
True
)
cancelled_at
=
Column
(
Integer
,
nullable
=
True
)
failed_at
=
Column
(
Integer
,
nullable
=
True
)
completed_at
=
Column
(
Integer
,
nullable
=
True
)
incomplete_details
=
Column
(
JSON
,
nullable
=
True
)
# get from assistant
model
=
Column
(
String
)
instructions
=
Column
(
Text
,
nullable
=
True
)
tools
=
Column
(
JSON
)
meta_data
=
Column
(
JSON
,
nullable
=
True
)
usage
=
Column
(
JSON
,
nullable
=
True
)
temperature
=
Column
(
Float
,
nullable
=
True
)
top_p
=
Column
(
Float
,
nullable
=
True
)
max_propmp_tokens
=
Column
(
Integer
,
nullable
=
True
)
truncation_strategy
=
Column
(
JSON
)
tool_choice
=
Column
(
JSON
)
response_format
=
Column
(
JSON
,
default
=
"auto"
)
thread
=
relationship
(
"Thread"
,
back_populates
=
"runs"
)
assistant
=
relationship
(
"Assistant"
,
back_populates
=
"runs"
)
message
=
relationship
(
"Message"
,
back_populates
=
"run"
)
ktransformers/server/models/assistants/threads.py
0 → 100644
View file @
18c42e67
from
sqlalchemy
import
JSON
,
Column
,
Integer
,
String
from
sqlalchemy.orm
import
relationship
from
ktransformers.server.utils.sql_utils
import
Base
class
Thread
(
Base
):
__tablename__
=
"threads"
id
=
Column
(
String
,
primary_key
=
True
,
index
=
True
)
object
=
Column
(
String
,
default
=
"thread"
)
created_at
=
Column
(
Integer
)
tool_resources
=
Column
(
JSON
,
nullable
=
True
)
meta_data
=
Column
(
JSON
,
nullable
=
True
)
runs
=
relationship
(
"Run"
,
back_populates
=
"thread"
)
messages
=
relationship
(
"Message"
,
back_populates
=
"thread"
)
ktransformers/server/requirements.txt
0 → 100644
View file @
18c42e67
torch >= 2.3.0,<=2.3.1
transformers == 4.43.2
fastapi >= 0.111.0
langchain >= 0.2.0
blessed >= 1.20.0
accelerate >= 0.31.0
sentencepiece >= 0.1.97
setuptools
build
ninja
wheel
colorlog
fire
\ No newline at end of file
ktransformers/server/schemas/__init__.py
0 → 100644
View file @
18c42e67
ktransformers/server/schemas/assistants/__init__.py
0 → 100644
View file @
18c42e67
ktransformers/server/schemas/assistants/assistants.py
0 → 100644
View file @
18c42e67
from
enum
import
Enum
from
time
import
time
from
typing
import
AsyncIterable
,
Callable
,
Dict
,
List
,
Optional
,
Union
from
asyncio
import
Lock
,
Queue
from
fastapi
import
logger
from
pydantic
import
BaseModel
,
Field
,
PrivateAttr
,
field_validator
,
model_validator
import
torch
from
ktransformers.server.config.config
import
Config
from
ktransformers.server.models.assistants.assistants
import
Assistant
from
ktransformers.server.models.assistants.threads
import
Thread
from
ktransformers.server.schemas.assistants.messages
import
Role
from
ktransformers.server.schemas.assistants.runs
import
RunObject
,
RunStreamResponse
,
ObjectWithCreatedTime
from
ktransformers.server.schemas.assistants.threads
import
ThreadObject
from
ktransformers.server.schemas.base
import
Metadata
,
MetadataField
,
ObjectID
from
ktransformers.server.schemas.assistants.tool
import
Tool
,
CodeInterpreter
,
FileSearch
,
RelatedThreads
,
FuntionTool
,
ToolResource
,
CodeInterpreterResource
,
FileSearchResource
,
RelatedThreadsResource
,
ToolType
from
ktransformers.server.utils.sql_utils
import
SQLUtil
class
AssistantBase
(
BaseModel
):
name
:
Optional
[
str
]
=
Field
(
None
,
description
=
'The name of the assistant.'
)
description
:
Optional
[
str
]
=
Field
(
None
,
description
=
'The description of the assistant.'
)
instructions
:
Optional
[
str
]
=
Field
(
None
,
description
=
'Instructions which is added in front of the input of LLM'
)
tools
:
List
[
Tool
]
=
Field
([],
max_length
=
128
)
@
field_validator
(
'tools'
,
mode
=
'before'
)
def
validate_tools
(
cls
,
value
):
re
=
[]
if
not
isinstance
(
value
,
list
):
raise
ValueError
(
'Invalid type for tools'
)
for
tool
in
value
:
if
'type'
not
in
tool
:
raise
ValueError
(
'Invalid type for tools'
)
if
tool
[
'type'
]
==
'code_interpreter'
:
re
.
append
(
CodeInterpreter
(
**
tool
))
elif
tool
[
'type'
]
==
'file_search'
:
re
.
append
(
FileSearch
(
**
tool
))
elif
tool
[
'type'
]
==
'related_threads'
:
re
.
append
(
RelatedThreads
(
**
tool
))
elif
tool
[
'type'
]
==
'function'
:
re
.
append
(
FuntionTool
(
**
tool
))
else
:
raise
ValueError
(
'Invalid type for tools'
)
return
re
tool_resources
:
List
[
ToolResource
]
=
Field
([],
max_length
=
128
)
@
field_validator
(
'tool_resources'
,
mode
=
'before'
)
def
validate_tool_resources
(
cls
,
value
):
re
=
[]
if
not
isinstance
(
value
,
list
):
raise
ValueError
(
'Invalid type for tool resources'
)
for
tool_re
in
value
:
if
'file_ids'
in
tool_re
:
re
.
append
(
CodeInterpreterResource
(
**
tool_re
))
elif
'vector_stores'
in
tool_re
:
re
.
append
(
FileSearchResource
(
**
tool_re
))
elif
'thread_ids'
in
tool_re
:
re
.
append
(
RelatedThreadsResource
(
**
tool_re
))
else
:
raise
ValueError
(
'Invalid type for tool resources'
)
return
re
meta_data
:
Metadata
=
MetadataField
@
model_validator
(
mode
=
'before'
)
def
convert_meta_data
(
cls
,
values
):
if
'meta_data'
in
values
:
values
[
'metadata'
]
=
values
[
'meta_data'
]
return
values
temperature
:
Optional
[
float
]
=
Field
(
ge
=
0.0
,
le
=
2.0
,
default
=
1
)
top_p
:
Optional
[
float
]
=
Field
(
ge
=
0.0
,
le
=
1.0
,
default
=
1
)
response_format
:
Union
[
str
,
Dict
[
str
,
str
]]
=
"auto"
class
AssistantCreate
(
AssistantBase
):
model
:
str
class
AssistantBuildStatus
(
BaseModel
):
class
Status
(
Enum
):
not_build
=
"not_build"
in_queue
=
"in_queue"
parsing
=
"parsing"
prefilling
=
"prefilling"
dumping
=
"dumping"
completed
=
"completed"
paused
=
"paused"
_lock
:
Lock
=
PrivateAttr
(
default_factory
=
Lock
)
_queue
:
Optional
[
Queue
]
=
PrivateAttr
(
None
)
status
:
Status
=
Field
(
default
=
Status
.
not_build
)
total_file_count
:
int
=
Field
(
default
=
0
)
parsed_file_count
:
int
=
Field
(
default
=
0
)
prefilling_current
:
int
=
Field
(
default
=
0
)
prefilling_total
:
int
=
Field
(
default
=
0
)
build_started_time
:
Optional
[
int
]
=
Field
(
default
=
None
)
build_completed_time
:
Optional
[
int
]
=
Field
(
default
=
None
)
# in megabytes
assistant_usage
:
int
=
Field
(
default
=
0
,
description
=
''
)
assistant_total_usage
:
int
=
Field
(
default
=
0
)
disk_free_space
:
int
=
Field
(
default
=
0
)
disk_total_space
:
int
=
Field
(
default
=
0
)
def
to_stream_reply
(
self
)
->
str
:
return
f
"event: assistant.build.status
\n
data:
{
self
.
model_dump_json
()
}
\n\n
"
class
AssistantObject
(
AssistantBase
,
ObjectWithCreatedTime
):
model
:
Optional
[
str
]
=
Field
(
default
=
Config
().
model_name
)
related_threads_objects
:
Optional
[
List
]
=
Field
(
None
,
exclude
=
True
)
_encoded_instruction
:
Optional
[
torch
.
Tensor
]
=
PrivateAttr
(
default
=
None
)
build_status
:
AssistantBuildStatus
=
Field
(
default
=
AssistantBuildStatus
())
def
as_api_response
(
self
):
return
self
.
model_dump
(
exclude
=
{
'build_status'
})
def
get_related_threads_ids
(
self
)
->
List
[
ObjectID
]:
re
=
[]
for
tool
,
tool_re
in
zip
(
self
.
tools
,
self
.
tool_resources
):
if
tool
.
type
==
ToolType
.
RELATED_THREADS
:
re
+=
tool_re
.
thread_ids
or
[]
return
re
def
get_related_threads_objects
(
self
)
->
List
:
# raise NotImplementedError # should be replaced
sql_utils
=
SQLUtil
()
if
self
.
related_threads_objects
is
None
:
with
sql_utils
.
get_db
()
as
db
:
db_threads
=
db
.
query
(
Thread
).
all
()
self
.
related_threads_objects
=
[
tool
for
tool
in
[
ThreadObject
.
model_validate
(
tool
.
__dict__
)
for
tool
in
db_threads
]
if
tool
.
is_related_threads
and
tool
.
meta_data
[
'assistant_id'
]
==
self
.
id
]
# logger.debug(
# f'Found {len(self.related_threads_objects)} related threads')
return
self
.
related_threads_objects
def
append_related_threads
(
self
,
thread_ids
:
List
[
ObjectID
]):
# logger.debug(f'{self.tools} {self.tool_resources}')
for
tool
,
tool_re
in
zip
(
self
.
tools
,
self
.
tool_resources
):
if
tool
.
type
==
ToolType
.
RELATED_THREADS
:
tool_re
.
thread_ids
+=
thread_ids
return
self
.
tools
.
append
(
RelatedThreads
(
type
=
ToolType
.
RELATED_THREADS
))
self
.
tool_resources
.
append
(
RelatedThreadsResource
(
thread_ids
=
thread_ids
))
async
def
update_build_status
(
self
,
events
:
AsyncIterable
)
->
AsyncIterable
:
async
for
event
in
events
:
# logger.debug(event)
if
isinstance
(
event
,
RunStreamResponse
):
if
event
.
event
==
RunObject
.
Status
.
completed
:
self
.
build_status
.
status
=
AssistantBuildStatus
.
Status
.
completed
self
.
build_status
.
build_completed_time
=
int
(
time
())
self
.
sync_db
()
yield
self
.
build_status
.
model_copy
()
elif
isinstance
(
event
,
dict
):
# logger.debug('dict')
if
'stage'
in
event
:
if
event
[
'stage'
]
==
'prefill'
:
self
.
build_status
.
status
=
AssistantBuildStatus
.
Status
.
prefilling
self
.
build_status
.
prefilling_current
=
event
[
'curr_progress'
]
self
.
build_status
.
prefilling_total
=
event
[
'max_progress'
]
if
event
[
'stage'
]
==
'parse'
:
self
.
build_status
.
status
=
AssistantBuildStatus
.
Status
.
parsing
self
.
build_status
.
parsed_file_count
=
event
[
'curr_progress'
]
self
.
build_status
.
total_file_count
=
event
[
'max_progress'
]
yield
self
.
build_status
.
model_copy
()
def
get_build_status
(
self
)
->
AssistantBuildStatus
:
return
self
.
build_status
def
sync_db
(
self
)
->
None
:
# raise NotImplementedError # should be replaced
sql_utils
=
SQLUtil
()
db_assistant
=
Assistant
(
**
self
.
model_dump
(
mode
=
'json'
),
)
with
sql_utils
.
get_db
()
as
db
:
sql_utils
.
db_merge_commit
(
db
,
db_assistant
)
def
get_encoded_instruction
(
self
,
encode_fn
:
Callable
)
->
torch
.
Tensor
:
if
self
.
_encoded_instruction
is
None
:
logger
.
info
(
f
'encoding assistant instruction:
{
self
.
instructions
}
'
)
self
.
_encoded_instruction
=
encode_fn
(
self
.
instructions
,
Role
.
user
)
return
self
.
_encoded_instruction
class
AssistantModify
(
AssistantBase
):
model
:
Optional
[
str
]
=
None
# Non API Backend
ktransformers/server/schemas/assistants/messages.py
0 → 100644
View file @
18c42e67
from
enum
import
Enum
from
typing
import
ForwardRef
,
List
,
Optional
,
Union
,
Callable
import
torch
from
pydantic
import
BaseModel
,
PrivateAttr
,
model_validator
from
ktransformers.server.exceptions
import
not_implemented
from
ktransformers.server.config.log
import
logger
from
ktransformers.server.models.assistants.messages
import
Message
from
ktransformers.server.schemas.base
import
Metadata
,
MetadataField
,
ObjectWithCreatedTime
from
ktransformers.server.schemas.assistants.tool
import
Field
,
CodeInterpreter
,
FileSearch
from
ktransformers.server.utils.sql_utils
import
SQLUtil
class
IncompleteDetails
(
BaseModel
):
reason
:
str
class
ContentType
(
Enum
):
image_file
=
"image_file"
image_url
=
"image_url"
text
=
"text"
class
ContentObject
(
BaseModel
):
type
:
ContentType
class
ImageFile
(
BaseModel
):
file_id
:
str
detail
:
str
class
ImageFileObject
(
ContentObject
):
image_file
:
ImageFile
class
ImageUrl
(
BaseModel
):
url
:
str
detail
:
str
class
ImageUrlObject
(
ContentObject
):
image_url
:
ImageUrl
class
Annotation
(
BaseModel
):
todo
:
str
class
Text
(
BaseModel
):
value
:
str
annotations
:
List
[
Annotation
]
=
Field
(
default
=
[])
class
TextObject
(
ContentObject
):
text
:
Text
delta_index
:
int
=
Field
(
default
=
0
,
exclude
=
True
)
special_tokens_on
:
bool
=
Field
(
default
=
False
,
exclude
=
True
)
last_two
:
str
=
Field
(
default
=
''
,
exclude
=
True
)
def
filter_append
(
self
,
text
:
str
):
self
.
text
.
value
+=
text
self
.
delta_index
+=
1
return
True
Content
=
Union
[
ImageFileObject
,
ImageUrlObject
,
TextObject
]
class
Attachment
(
BaseModel
):
file_id
:
Optional
[
str
]
=
Field
(
default
=
None
)
tools
:
Optional
[
List
[
Union
[
CodeInterpreter
,
FileSearch
]]]
=
Field
(
default
=
None
)
class
Role
(
Enum
):
user
=
"user"
assistant
=
"assistant"
def
is_user
(
self
)
->
bool
:
return
self
==
Role
.
user
class
MessageCore
(
BaseModel
):
role
:
Role
content
:
List
[
Content
]
attachments
:
Optional
[
List
[
Attachment
]]
meta_data
:
Metadata
=
MetadataField
@
model_validator
(
mode
=
'before'
)
@
classmethod
def
convert_meta_data
(
cls
,
values
):
if
'meta_data'
in
values
:
values
[
'metadata'
]
=
values
[
'meta_data'
]
return
values
class
MessageBase
(
MessageCore
):
class
Status
(
Enum
):
created
=
"created"
# only used for stream
in_progress
=
"in_progress"
incomplete
=
"incomplete"
completed
=
"completed"
thread_id
:
str
status
:
Status
incomplete_details
:
Optional
[
IncompleteDetails
]
=
None
completed_at
:
Optional
[
int
]
=
None
incomplete_at
:
Optional
[
int
]
=
None
assistant_id
:
Optional
[
str
]
=
None
run_id
:
Optional
[
str
]
MessageStreamResponse
=
ForwardRef
(
'MessageStreamResponse'
)
class
MessageObject
(
MessageBase
,
ObjectWithCreatedTime
):
_encoded_content
:
Optional
[
torch
.
Tensor
]
=
PrivateAttr
(
default
=
None
)
def
get_text_content
(
self
)
->
str
:
text_content
=
""
for
content
in
self
.
content
:
if
content
.
type
==
ContentType
.
text
:
text_content
+=
content
.
text
.
value
else
:
raise
not_implemented
(
"Content other than text"
)
return
text_content
async
def
get_encoded_content
(
self
,
encode_fn
:
Callable
):
if
self
.
_encoded_content
is
None
:
logger
.
info
(
f
'encoding
{
self
.
role
.
value
}
message(
{
self
.
status
.
value
}
):
{
self
.
get_text_content
()
}
'
)
self
.
_encoded_content
=
encode_fn
(
self
.
get_text_content
(),
self
.
role
)
for
f
in
self
.
get_attached_files
():
logger
.
info
(
f
'encoding file:
{
f
.
filename
}
'
)
self
.
_encoded_content
=
torch
.
cat
([
self
.
_encoded_content
,
encode_fn
(
await
f
.
get_str
(),
self
.
role
)],
dim
=-
1
)
yield
None
yield
self
.
_encoded_content
def
get_attached_files
(
self
):
raise
NotImplementedError
# should be replaced
def
append_message_delta
(
self
,
text
:
str
):
raise
NotImplementedError
# should be replaced
def
sync_db
(
self
):
# raise NotImplementedError # should be replaced
sql_utils
=
SQLUtil
()
db_message
=
Message
(
**
self
.
model_dump
(
mode
=
"json"
),
)
with
sql_utils
.
get_db
()
as
db
:
sql_utils
.
db_merge_commit
(
db
,
db_message
)
def
stream_response_with_event
(
self
,
event
:
MessageBase
.
Status
)
->
MessageStreamResponse
:
match
event
:
case
MessageObject
.
Status
.
created
:
self
.
status
=
MessageObject
.
Status
.
in_progress
case
_
:
self
.
status
=
event
return
MessageStreamResponse
(
message
=
self
,
event
=
event
)
class
MessageStreamResponse
(
BaseModel
):
message
:
MessageObject
event
:
MessageObject
.
Status
def
to_stream_reply
(
self
):
return
f
"event: thread.message.
{
self
.
event
.
value
}
\n
data:
{
self
.
message
.
model_dump_json
()
}
\n\n
"
class
MessageCreate
(
BaseModel
):
role
:
Role
=
Field
(
default
=
Role
.
user
)
content
:
Union
[
str
|
List
[
Content
]]
attachments
:
Optional
[
List
[
Attachment
]]
=
None
meta_data
:
Metadata
=
MetadataField
@
model_validator
(
mode
=
'before'
)
@
classmethod
def
convert_meta_data
(
cls
,
values
):
if
'meta_data'
in
values
:
values
[
'metadata'
]
=
values
[
'meta_data'
]
return
values
def
to_core
(
self
)
->
MessageCore
:
# logger.debug(f"Converting message create to core {self.model_dump()}")
core
=
MessageCore
(
role
=
self
.
role
,
content
=
[],
attachments
=
self
.
attachments
,
meta_data
=
self
.
meta_data
,
)
if
isinstance
(
self
.
content
,
str
):
core
.
content
=
[
TextObject
(
type
=
"text"
,
text
=
Text
(
value
=
self
.
content
,
annotations
=
[]))]
elif
isinstance
(
self
.
content
,
list
):
core
.
content
=
self
.
content
else
:
raise
ValueError
(
"Invalid content type"
)
return
core
class
MessageModify
(
BaseModel
):
meta_data
:
Metadata
=
MetadataField
@
model_validator
(
mode
=
'before'
)
@
classmethod
def
convert_meta_data
(
cls
,
values
):
if
'meta_data'
in
values
:
values
[
'metadata'
]
=
values
[
'meta_data'
]
return
values
Prev
1
…
3
4
5
6
7
8
9
10
11
…
13
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment