Skip to content

Commit be38d4a

Browse files
authored
Merge branch 'main' into dependabot/pip/pip-6e802688c2
2 parents c9a6939 + ece1d91 commit be38d4a

File tree

187 files changed

+24903
-1404
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

187 files changed

+24903
-1404
lines changed

.github/workflows/docker-image.yml renamed to .github/workflows/docker-image-api.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
name: Docker Image CI
1+
name: API Docker Image CI
22

33
on:
44
push:
5+
paths-ignore:
6+
- 'playground-ui/**'
57
branches:
68
- 'main'
79
release:
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Playground UI Docker Image CI
2+
3+
on:
4+
push:
5+
paths:
6+
- 'playground-ui/**'
7+
branches:
8+
- 'main'
9+
release:
10+
types: [ published ]
11+
12+
env:
13+
IMAGE_NAME: 'samepaage/open-assistant-playground-ui'
14+
15+
jobs:
16+
build-and-push:
17+
runs-on: ubuntu-latest
18+
permissions:
19+
packages: write
20+
contents: read
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- name: Set up QEMU
25+
uses: docker/setup-qemu-action@v3
26+
27+
- name: Set up Docker Buildx
28+
uses: docker/setup-buildx-action@v3
29+
30+
- name: Login to DockerHub
31+
uses: docker/login-action@v2
32+
with:
33+
username: ${{ secrets.DOCKERHUB_USER }}
34+
password: ${{ secrets.DOCKERHUB_TOKEN }}
35+
36+
- name: Extract metadata (tags, labels) for Docker
37+
id: meta
38+
uses: docker/metadata-action@v5
39+
with:
40+
images: ${{ env.IMAGE_NAME }}
41+
tags: |
42+
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
43+
type=ref,event=branch
44+
type=sha,enable=true,priority=100,prefix=,suffix=,format=long
45+
type=raw,value=${{ github.ref_name }},enable=${{ startsWith(github.ref, 'refs/tags/') }}
46+
47+
- name: Build and push
48+
uses: docker/build-push-action@v5
49+
with:
50+
context: ./playground-ui
51+
platforms: |
52+
linux/amd64
53+
linux/arm64
54+
build-args: |
55+
COMMIT_SHA=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
56+
push: true
57+
tags: ${{ steps.meta.outputs.tags }}
58+
labels: ${{ steps.meta.outputs.labels }}
59+
cache-from: type=gha
60+
cache-to: type=gha,mode=max

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ RUN pip install --no-cache-dir poetry \
1313
COPY poetry.lock /env/poetry.lock
1414
COPY pyproject.toml /env/pyproject.toml
1515

16-
RUN cd /env && poetry install --no-dev
16+
RUN cd /env && poetry lock --no-update && poetry install --only main
1717

1818
EXPOSE 8086
1919

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ assistant = client.beta.assistants.create(
4949
| Built-in Tool | Extendable | Not Extendable |
5050
| Code Interpreter | Under Development | Supported |
5151
| LLM Support | Supports More LLMs | Only GPT |
52-
| Message Streaming Output | Supports | Not Supported |
52+
| Message Streaming Output | Supports | Supported |
5353
| Local Deployment | Supported | Not Supported |
5454

5555
- **LLM Support**: Compared to the official OpenAI version, more models can be supported by integrating with One API.
@@ -92,15 +92,17 @@ Interface documentation address: http://127.0.0.1:8086/docs
9292

9393
### Complete Usage Example
9494

95-
In this example, an AI assistant is created and run using the official OpenAI client library, including two built-in
96-
tools, web_search and retrieval, and a custom function.
95+
In this example, an AI assistant is created and run using the official OpenAI client library. If you need to explore other usage methods,
96+
such as streaming output, tools (web_search, retrieval, function), etc., you can find the corresponding code under the examples directory.
9797
Before running, you need to run `pip install openai` to install the Python `openai` library.
9898

9999
```sh
100100
# !pip install openai
101-
python tests/e2e/index.py
101+
export PYTHONPATH=$(pwd)
102+
python examples/run_assistant.py
102103
```
103104

105+
104106
### Permissions
105107
Simple user isolation is provided based on tokens to meet SaaS deployment requirements. It can be enabled by configuring `APP_AUTH_ENABLE`.
106108

README_CN.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ assistant = client.beta.assistants.create(
4848
| 内置 Tool | 支持扩展 | 不支持扩展 |
4949
| Code Interpreter | 待开发 | 支持 |
5050
| LLM 支持 | 支持更多的 LLM | 仅 GPT |
51-
| Message 流式输出 | 支持 | 不支持 |
51+
| Message 流式输出 | 支持 | 支持 |
5252
| 本地部署 | 支持 | 不支持 |
5353

5454
- **LLM 支持**: 相较于 OpenAI 官方版本,可以通过接入 One API 来支持更多的模型。
@@ -89,12 +89,14 @@ Api Base URL: http://127.0.0.1:8086/api/v1
8989

9090
### 完整使用示例
9191

92-
此示例中使用 OpenAI 官方的 client 库创建并运行了一个 AI 助手,包含了 web_search 和 retrieval 两个内置 tool 和一个自定义 function。
92+
此示例中使用 OpenAI 官方的 client 库创建并运行了一个 AI 助手。如果需要查看其它使用方式,如流式输出、工具(web_search、retrieval、function)的使用等,
93+
可以在 examples 查看对应示例。
9394
运行之前需要运行 `pip install openai` 安装 Python `openai` 库。
9495

9596
```sh
9697
# !pip install openai
97-
python tests/e2e/index.py
98+
export PYTHONPATH=$(pwd)
99+
python examples/run_assistant.py
98100
```
99101

100102
### 权限

app/api/v1/action.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from app.api.deps import get_async_session, get_token_id
88
from app.libs.paginate import cursor_page, CommonPage
9-
from app.models.action import Action
9+
from app.models.action import Action, ActionRead
1010
from app.models.token_relation import RelationType
1111
from app.providers.auth_provider import auth_policy
1212
from app.schemas.common import DeleteResponse, BaseSuccessDataResponse
@@ -16,44 +16,50 @@
1616
router = APIRouter()
1717

1818

19-
@router.get("", response_model=CommonPage[Action])
19+
@router.get("", response_model=CommonPage[ActionRead])
2020
async def list_actions(*, session: AsyncSession = Depends(get_async_session), token_id=Depends(get_token_id)):
2121
"""
2222
Returns a list of Actions.
2323
"""
2424
statement = auth_policy.token_filter(
2525
select(Action), field=Action.id, relation_type=RelationType.Action, token_id=token_id
2626
)
27-
return await cursor_page(statement, session)
27+
page = await cursor_page(statement, session)
28+
page.data = [ast.model_dump(by_alias=True) for ast in page.data]
29+
return page.model_dump(by_alias=True)
2830

2931

30-
@router.post("", response_model=List[Action])
32+
@router.post("", response_model=List[ActionRead])
3133
async def create_actions(
3234
*, session: AsyncSession = Depends(get_async_session), body: ActionBulkCreateRequest, token_id=Depends(get_token_id)
33-
) -> List[Action]:
35+
):
3436
"""
3537
Create an action with openapi schema.
3638
"""
3739

38-
return await ActionService.create_actions(session=session, body=body, token_id=token_id)
40+
actions = await ActionService.create_actions(session=session, body=body, token_id=token_id)
41+
actions = [item.model_dump(by_alias=True) for item in actions]
42+
return actions
3943

4044

41-
@router.get("/{action_id}", response_model=Action)
42-
async def get_action(*, session: AsyncSession = Depends(get_async_session), action_id: str) -> Action:
45+
@router.get("/{action_id}", response_model=ActionRead)
46+
async def get_action(*, session: AsyncSession = Depends(get_async_session), action_id: str):
4347
"""
4448
Retrieves an action.
4549
"""
46-
return await ActionService.get_action(session=session, action_id=action_id)
50+
action = await ActionService.get_action(session=session, action_id=action_id)
51+
return action.model_dump(by_alias=True)
4752

4853

49-
@router.post("/{action_id}", response_model=Action)
54+
@router.post("/{action_id}", response_model=ActionRead)
5055
async def modify_action(
5156
*, session: AsyncSession = Depends(get_async_session), action_id: str, body: ActionUpdateRequest
52-
) -> Action:
57+
):
5358
"""
5459
Modifies an action.
5560
"""
56-
return await ActionService.modify_action(session=session, action_id=action_id, body=body)
61+
action = await ActionService.modify_action(session=session, action_id=action_id, body=body)
62+
return action.model_dump(by_alias=True)
5763

5864

5965
@router.delete("/{action_id}", response_model=DeleteResponse)

app/api/v1/assistant.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from sqlmodel import select
44

55
from app.api.deps import get_token_id, get_async_session
6-
from app.models.assistant import Assistant, AssistantUpdate, AssistantCreate
6+
from app.models.assistant import Assistant, AssistantUpdate, AssistantCreate, AssistantRead
77
from app.libs.paginate import cursor_page, CommonPage
88
from app.models.token_relation import RelationType
99
from app.providers.auth_provider import auth_policy
@@ -13,43 +13,48 @@
1313
router = APIRouter()
1414

1515

16-
@router.get("", response_model=CommonPage[Assistant])
16+
@router.get("", response_model=CommonPage[AssistantRead])
1717
async def list_assistants(*, session: AsyncSession = Depends(get_async_session), token_id=Depends(get_token_id)):
1818
"""
1919
Returns a list of assistants.
2020
"""
2121
statement = auth_policy.token_filter(
2222
select(Assistant), field=Assistant.id, relation_type=RelationType.Assistant, token_id=token_id
2323
)
24-
return await cursor_page(statement, session)
24+
asts_page = await cursor_page(statement, session)
25+
asts_page.data = [ast.model_dump(by_alias=True) for ast in asts_page.data]
26+
return asts_page
2527

2628

27-
@router.post("", response_model=Assistant)
29+
@router.post("", response_model=AssistantRead)
2830
async def create_assistant(
2931
*, session: AsyncSession = Depends(get_async_session), body: AssistantCreate, token_id=Depends(get_token_id)
30-
) -> Assistant:
32+
):
3133
"""
3234
Create an assistant with a model and instructions.
3335
"""
34-
return await AssistantService.create_assistant(session=session, body=body, token_id=token_id)
36+
ast = await AssistantService.create_assistant(session=session, body=body, token_id=token_id)
37+
return ast.model_dump(by_alias=True)
3538

3639

37-
@router.get("/{assistant_id}", response_model=Assistant)
38-
async def get_assistant(*, session: AsyncSession = Depends(get_async_session), assistant_id: str) -> Assistant:
40+
@router.get("/{assistant_id}", response_model=AssistantRead)
41+
async def get_assistant(*, session: AsyncSession = Depends(get_async_session), assistant_id: str):
3942
"""
4043
Retrieves an assistant.
4144
"""
42-
return await AssistantService.get_assistant(session=session, assistant_id=assistant_id)
45+
ast = await AssistantService.get_assistant(session=session, assistant_id=assistant_id)
46+
return ast.model_dump(by_alias=True)
4347

4448

45-
@router.post("/{assistant_id}", response_model=Assistant)
49+
@router.post("/{assistant_id}", response_model=AssistantRead)
4650
async def modify_assistant(
4751
*, session: AsyncSession = Depends(get_async_session), assistant_id: str, body: AssistantUpdate
48-
) -> Assistant:
52+
):
4953
"""
5054
Modifies an assistant.
5155
"""
52-
return await AssistantService.modify_assistant(session=session, assistant_id=assistant_id, body=body)
56+
ast = await AssistantService.modify_assistant(session=session, assistant_id=assistant_id, body=body)
57+
return ast.model_dump(by_alias=True)
5358

5459

5560
@router.delete("/{assistant_id}", response_model=DeleteResponse)

app/api/v1/message.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
from typing import Optional
12
from fastapi import APIRouter, Depends
3+
from fastapi.params import Query
24
from sqlalchemy.ext.asyncio import AsyncSession
35
from sqlmodel import select
46

57
from app.api.deps import get_async_session
68
from app.models import MessageFile
7-
from app.models.message import Message, MessageCreate, MessageUpdate
9+
from app.models.message import Message, MessageCreate, MessageUpdate, MessageRead
810
from app.libs.paginate import cursor_page, CommonPage
911
from app.services.message.message import MessageService
1012

@@ -13,57 +15,68 @@
1315

1416
@router.get(
1517
"/{thread_id}/messages",
16-
response_model=CommonPage[Message],
18+
response_model=CommonPage[MessageRead],
1719
)
1820
async def list_messages(
1921
*,
2022
session: AsyncSession = Depends(get_async_session),
2123
thread_id: str,
24+
run_id: Optional[str] = Query(None, description="Filter messages by the run ID that generated them."),
2225
):
2326
"""
2427
Returns a list of messages for a given thread.
2528
"""
26-
return await cursor_page(select(Message).where(Message.thread_id == thread_id), session)
29+
statement = select(Message).where(Message.thread_id == thread_id)
30+
if run_id:
31+
# 根据 run_id 进行过滤
32+
statement = statement.where(Message.run_id == run_id)
2733

34+
page = await cursor_page(statement, session)
35+
page.data = [ast.model_dump(by_alias=True) for ast in page.data]
36+
return page
2837

29-
@router.post("/{thread_id}/messages", response_model=Message)
38+
39+
@router.post("/{thread_id}/messages", response_model=MessageRead)
3040
async def create_message(
3141
*, session: AsyncSession = Depends(get_async_session), thread_id: str, body: MessageCreate
32-
) -> Message:
42+
):
3343
"""
3444
Create a message.
3545
"""
36-
return await MessageService.create_message(session=session, thread_id=thread_id, body=body)
46+
message = await MessageService.create_message(session=session, thread_id=thread_id, body=body)
47+
return message.model_dump(by_alias=True)
3748

3849

3950
@router.get(
4051
"/{thread_id}/messages/{message_id}",
41-
response_model=Message,
52+
response_model=MessageRead,
4253
)
4354
async def get_message(
4455
*, session: AsyncSession = Depends(get_async_session), thread_id: str, message_id: str
45-
) -> Message:
56+
):
4657
"""
4758
Retrieve a message.
4859
"""
49-
return await MessageService.get_message(session=session, thread_id=thread_id, message_id=message_id)
60+
message = await MessageService.get_message(session=session, thread_id=thread_id, message_id=message_id)
61+
return message.model_dump(by_alias=True)
5062

5163

5264
@router.post(
5365
"/{thread_id}/messages/{message_id}",
54-
response_model=Message,
66+
response_model=MessageRead,
5567
)
5668
async def modify_message(
5769
*,
5870
session: AsyncSession = Depends(get_async_session),
5971
thread_id: str,
6072
message_id: str = ...,
6173
body: MessageUpdate = ...,
62-
) -> Message:
74+
):
6375
"""
6476
Modifies a message.
6577
"""
66-
return await MessageService.modify_message(session=session, thread_id=thread_id, message_id=message_id, body=body)
78+
message = await MessageService.modify_message(session=session, thread_id=thread_id, message_id=message_id, body=body)
79+
return message.model_dump(by_alias=True)
6780

6881

6982
@router.get(

0 commit comments

Comments
 (0)