Files
wehub-resource-sync b7f52be4c9
CI / Run CI (push) Has been cancelled
CI / check-backend (push) Has been cancelled
CI / check-frontend (push) Has been cancelled
CI / tests (push) Has been cancelled
CI / e2e-tests (push) Has been cancelled
Copilot Setup Steps / copilot-setup-steps (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:48:47 +08:00

1064 lines
33 KiB
Python

import datetime
import uuid
from unittest.mock import ANY, AsyncMock, Mock, patch
import pytest
from httpx import HTTPStatusError, RequestError
from literalai import (
AsyncLiteralClient,
Attachment,
Attachment as LiteralAttachment,
PageInfo,
PaginatedResponse,
Score as LiteralScore,
Step as LiteralStep,
Thread,
Thread as LiteralThread,
User as LiteralUser,
UserDict,
)
from literalai.api import AsyncLiteralAPI
from literalai.observability.step import (
AttachmentDict as LiteralAttachmentDict,
StepDict as LiteralStepDict,
)
from literalai.observability.thread import ThreadDict as LiteralThreadDict
from chainlit.data.literalai import LiteralDataLayer, LiteralToChainlitConverter
from chainlit.element import Audio, File, Image, Pdf, Text, Video
from chainlit.step import Step, StepDict
from chainlit.types import (
Feedback,
Pagination,
ThreadFilter,
)
from chainlit.user import PersistedUser, User
@pytest.fixture
async def mock_literal_client(monkeypatch: pytest.MonkeyPatch):
client = Mock(spec=AsyncLiteralClient)
client.api = AsyncMock(spec=AsyncLiteralAPI)
monkeypatch.setattr("literalai.AsyncLiteralClient", client)
return client
@pytest.fixture
async def literal_data_layer(mock_literal_client):
data_layer = LiteralDataLayer(api_key="fake_api_key", server="https://fake.server")
data_layer.client = mock_literal_client
return data_layer
@pytest.fixture
def test_thread():
return LiteralThread.from_dict(
{
"id": "test_thread_id",
"name": "Test Thread",
"createdAt": "2023-01-01T00:00:00Z",
"metadata": {},
"participant": {},
"steps": [],
"tags": [],
}
)
@pytest.fixture
def test_step_dict(test_thread) -> StepDict:
return {
"createdAt": "2023-01-01T00:00:00Z",
"start": "2023-01-01T00:00:00Z",
"end": "2023-01-01T00:00:00Z",
"generation": {},
"id": "test_step_id",
"name": "Test Step",
"threadId": test_thread.id,
"type": "user_message",
"tags": [],
"metadata": {"key": "value"},
"input": "test input",
"output": "test output",
"waitForAnswer": True,
"showInput": True,
"language": "en",
}
@pytest.fixture
def test_step(test_thread: LiteralThread):
return LiteralStep.from_dict(
{
"id": str(uuid.uuid4()),
"name": "Test Step",
"type": "user_message",
"environment": None,
"threadId": test_thread.id,
"error": None,
"input": {},
"output": {},
"metadata": {},
"tags": [],
"parentId": None,
"createdAt": "2023-01-01T00:00:00Z",
"startTime": "2023-01-01T00:00:00Z",
"endTime": "2023-01-01T00:00:00Z",
"generation": {},
"scores": [],
"attachments": [],
"rootRunId": None,
}
)
@pytest.fixture
def literal_test_user(test_user: User):
return LiteralUser(
id=str(uuid.uuid4()),
created_at=datetime.datetime.now().isoformat(),
identifier=test_user.identifier,
metadata=test_user.metadata,
)
@pytest.fixture
def test_filters() -> ThreadFilter:
return ThreadFilter(feedback=1, userId="user1", search="test")
@pytest.fixture
def test_pagination() -> Pagination:
return Pagination(first=10, cursor=None)
@pytest.fixture
def test_attachment(
test_thread: LiteralThread, test_step: LiteralStep
) -> LiteralAttachment:
return Attachment(
id="test_attachment_id",
step_id=test_step.id,
thread_id=test_thread.id,
metadata={
"display": "side",
"language": "python",
"type": "file",
},
mime="text/plain",
name="test_file.txt",
object_key="test_object_key",
url="https://example.com/test_file.txt",
)
async def test_create_step(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
test_step_dict: StepDict,
mock_chainlit_context,
):
async with mock_chainlit_context:
await literal_data_layer.create_step(test_step_dict)
mock_literal_client.api.send_steps.assert_awaited_once_with(
[
{
"createdAt": "2023-01-01T00:00:00Z",
"startTime": "2023-01-01T00:00:00Z",
"endTime": "2023-01-01T00:00:00Z",
"generation": {},
"id": "test_step_id",
"parentId": None,
"name": "Test Step",
"threadId": "test_thread_id",
"type": "user_message",
"tags": [],
"metadata": {
"key": "value",
"waitForAnswer": True,
"language": "en",
"showInput": True,
},
"input": {"content": "test input"},
"output": {"content": "test output"},
}
]
)
async def test_safely_send_steps_success(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
mock_chainlit_context,
):
test_steps = [{"id": "test_step_id", "name": "Test Step"}]
async with mock_chainlit_context:
await literal_data_layer.safely_send_steps(test_steps)
mock_literal_client.api.send_steps.assert_awaited_once_with(test_steps)
async def test_safely_send_steps_http_status_error(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
mock_chainlit_context,
caplog,
):
test_steps = [{"id": "test_step_id", "name": "Test Step"}]
mock_literal_client.api.send_steps.side_effect = HTTPStatusError(
"HTTP Error", request=Mock(), response=Mock(status_code=500)
)
async with mock_chainlit_context:
await literal_data_layer.safely_send_steps(test_steps)
mock_literal_client.api.send_steps.assert_awaited_once_with(test_steps)
assert "HTTP Request: error sending steps: 500" in caplog.text
async def test_safely_send_steps_request_error(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
mock_chainlit_context,
caplog,
):
test_steps = [{"id": "test_step_id", "name": "Test Step"}]
mock_request = Mock()
mock_request.url = "https://example.com/api"
mock_literal_client.api.send_steps.side_effect = RequestError(
"Request Error", request=mock_request
)
async with mock_chainlit_context:
await literal_data_layer.safely_send_steps(test_steps)
mock_literal_client.api.send_steps.assert_awaited_once_with(test_steps)
assert "HTTP Request: error for 'https://example.com/api'." in caplog.text
async def test_get_user(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
literal_test_user: LiteralUser,
persisted_test_user: PersistedUser,
):
mock_literal_client.api.get_user.return_value = literal_test_user
user = await literal_data_layer.get_user("test_user_id")
assert user is not None
assert user.id == literal_test_user.id
assert user.identifier == literal_test_user.identifier
mock_literal_client.api.get_user.assert_awaited_once_with(identifier="test_user_id")
async def test_get_user_not_found(
literal_data_layer: LiteralDataLayer, mock_literal_client: Mock
):
mock_literal_client.api.get_user.return_value = None
user = await literal_data_layer.get_user("non_existent_user_id")
assert user is None
mock_literal_client.api.get_user.assert_awaited_once_with(
identifier="non_existent_user_id"
)
async def test_create_user_not_existing(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
test_user: User,
literal_test_user: LiteralUser,
):
mock_literal_client.api.get_user.return_value = None
mock_literal_client.api.create_user.return_value = literal_test_user
persisted_user = await literal_data_layer.create_user(test_user)
mock_literal_client.api.create_user.assert_awaited_once_with(
identifier=test_user.identifier, metadata=test_user.metadata
)
assert persisted_user is not None
assert isinstance(persisted_user, PersistedUser)
assert persisted_user.id == literal_test_user.id
assert persisted_user.identifier == literal_test_user.identifier
async def test_create_user_update_existing(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
test_user: User,
literal_test_user: LiteralUser,
persisted_test_user: PersistedUser,
):
mock_literal_client.api.get_user.return_value = literal_test_user
persisted_user = await literal_data_layer.create_user(test_user)
mock_literal_client.api.create_user.assert_not_called()
mock_literal_client.api.update_user.assert_awaited_once_with(
id=literal_test_user.id, metadata=test_user.metadata
)
assert persisted_user is not None
assert isinstance(persisted_user, PersistedUser)
assert persisted_user.id == literal_test_user.id
assert persisted_user.identifier == literal_test_user.identifier
async def test_create_user_id_none(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
test_user: User,
literal_test_user: LiteralUser,
):
"""Weird edge case; persisted user without an id. Do we need this!??"""
literal_test_user.id = None
mock_literal_client.api.get_user.return_value = literal_test_user
persisted_user = await literal_data_layer.create_user(test_user)
mock_literal_client.api.create_user.assert_not_called()
mock_literal_client.api.update_user.assert_not_called()
assert persisted_user is not None
assert isinstance(persisted_user, PersistedUser)
assert persisted_user.id == ""
assert persisted_user.identifier == literal_test_user.identifier
async def test_update_thread(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
test_thread: LiteralThread,
):
await literal_data_layer.update_thread(test_thread.id, name=test_thread.name)
mock_literal_client.api.upsert_thread.assert_awaited_once_with(
id=test_thread.id,
name=test_thread.name,
participant_id=None,
metadata=None,
tags=None,
)
async def test_get_thread_author(
literal_data_layer, mock_literal_client: Mock, test_thread: LiteralThread
):
test_thread.participant_identifier = "test_user_identifier"
mock_literal_client.api.get_thread.return_value = test_thread
author = await literal_data_layer.get_thread_author(test_thread.id)
assert author == "test_user_identifier"
mock_literal_client.api.get_thread.assert_awaited_once_with(id=test_thread.id)
async def test_get_thread(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
test_thread: LiteralThread,
test_step: LiteralStep,
):
assert isinstance(test_thread.steps, list)
test_thread.steps.append(test_step)
mock_literal_client.api.get_thread.return_value = test_thread
thread = await literal_data_layer.get_thread(test_thread.id)
mock_literal_client.api.get_thread.assert_awaited_once_with(id=test_thread.id)
assert thread is not None
assert thread["id"] == test_thread.id
assert thread["name"] == test_thread.name
assert len(thread["steps"]) == 1
assert thread["steps"][0].get("id") == test_step.id
async def test_get_thread_with_stub_step(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
test_thread: LiteralThread,
):
# Create a step that should be stubbed
stub_step = LiteralStep.from_dict(
{
"id": "stub_step_id",
"name": "Stub Step",
"type": "undefined",
"threadId": test_thread.id,
"createdAt": "2023-01-01T00:00:00Z",
}
)
test_thread.steps = [stub_step]
mock_literal_client.api.get_thread.return_value = test_thread
# Mock the config.ui.cot value to ensure check_add_step_in_cot returns False
with patch("chainlit.config.config.ui.cot", "hidden"):
thread = await literal_data_layer.get_thread(test_thread.id)
mock_literal_client.api.get_thread.assert_awaited_once_with(id=test_thread.id)
assert thread is not None
assert thread["id"] == test_thread.id
assert thread["name"] == test_thread.name
assert len(thread["steps"]) == 1
assert thread["steps"][0].get("id") == "stub_step_id"
assert thread["steps"][0].get("type") == "undefined"
assert thread["steps"][0].get("input") == ""
assert thread["steps"][0].get("output") == ""
# Additional assertions to ensure the step is stubbed
assert "metadata" not in thread["steps"][0]
assert "createdAt" not in thread["steps"][0]
async def test_get_thread_with_attachment(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
test_thread: LiteralThread,
test_step: LiteralStep,
test_attachment: LiteralAttachment,
):
# Add the attachment to the test step
test_step.attachments = [test_attachment]
test_thread.steps = [test_step]
mock_literal_client.api.get_thread.return_value = test_thread
thread = await literal_data_layer.get_thread(test_thread.id)
mock_literal_client.api.get_thread.assert_awaited_once_with(id=test_thread.id)
assert thread is not None
assert thread["id"] == test_thread.id
assert thread["name"] == test_thread.name
assert thread["steps"] is not None
assert len(thread["steps"]) == 1
assert thread["elements"] is not None
assert len(thread["elements"]) == 1
element = thread["elements"][0] if thread["elements"] else None
assert element is not None
assert element["id"] == "test_attachment_id"
assert element["forId"] == test_step.id
assert element["threadId"] == test_thread.id
assert element["type"] == "file"
assert element["display"] == "side"
assert element["language"] == "python"
assert element["mime"] == "text/plain"
assert element["name"] == "test_file.txt"
assert element["objectKey"] == "test_object_key"
assert element["url"] == "https://example.com/test_file.txt"
async def test_get_thread_non_existing(
literal_data_layer: LiteralDataLayer, mock_literal_client: Mock
):
mock_literal_client.api.get_thread.return_value = None
thread = await literal_data_layer.get_thread("non_existent_thread_id")
mock_literal_client.api.get_thread.assert_awaited_once_with(
id="non_existent_thread_id"
)
assert thread is None
async def test_delete_thread(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
test_thread: LiteralThread,
):
await literal_data_layer.delete_thread(test_thread.id)
mock_literal_client.api.delete_thread.assert_awaited_once_with(id=test_thread.id)
async def test_list_threads(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
test_filters: ThreadFilter,
test_pagination: Pagination,
):
response: PaginatedResponse[Thread] = PaginatedResponse(
page_info=PageInfo(
has_next_page=True, start_cursor="start_cursor", end_cursor="end_cursor"
),
data=[
Thread(
id="thread1",
name="Thread 1",
),
Thread(
id="thread2",
name="Thread 2",
),
],
)
mock_literal_client.api.list_threads.return_value = response
result = await literal_data_layer.list_threads(test_pagination, test_filters)
mock_literal_client.api.list_threads.assert_awaited_once_with(
first=10,
after=None,
filters=[
{"field": "participantId", "operator": "eq", "value": "user1"},
{
"field": "stepOutput",
"operator": "ilike",
"value": "test",
"path": "content",
},
{
"field": "scoreValue",
"operator": "eq",
"value": 1,
"path": "user-feedback",
},
],
order_by={"column": "createdAt", "direction": "DESC"},
)
assert result.pageInfo.hasNextPage
assert result.pageInfo.startCursor == "start_cursor"
assert result.pageInfo.endCursor == "end_cursor"
assert len(result.data) == 2
assert result.data[0]["id"] == "thread1"
assert result.data[1]["id"] == "thread2"
async def test_create_element(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
mock_chainlit_context,
):
mock_literal_client.api.upload_file.return_value = {"object_key": "test_object_key"}
async with mock_chainlit_context:
text_element = Text(
id=str(uuid.uuid4()),
name="test.txt",
mime="text/plain",
content="test content",
for_id="test_step_id",
)
await literal_data_layer.create_element(text_element)
mock_literal_client.api.upload_file.assert_awaited_once_with(
content=text_element.content,
mime=text_element.mime,
thread_id=text_element.thread_id,
)
mock_literal_client.api.send_steps.assert_awaited_once_with(
[
{
"id": text_element.for_id,
"threadId": text_element.thread_id,
"attachments": [
{
"id": ANY,
"name": text_element.name,
"metadata": {
"size": None,
"language": None,
"display": text_element.display,
"type": text_element.type,
"page": None,
"props": None,
},
"mime": text_element.mime,
"url": None,
"objectKey": "test_object_key",
}
],
}
]
)
async def test_get_element(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
test_attachment: LiteralAttachment,
):
mock_literal_client.api.get_attachment.return_value = test_attachment
element_dict = await literal_data_layer.get_element(
"test_thread_id", "test_element_id"
)
mock_literal_client.api.get_attachment.assert_awaited_once_with(
id="test_element_id"
)
assert element_dict is not None
# Compare element_dict attributes to attachment attributes
assert element_dict["id"] == test_attachment.id
assert element_dict["forId"] == test_attachment.step_id
assert element_dict["threadId"] == test_attachment.thread_id
assert element_dict["name"] == test_attachment.name
assert element_dict["mime"] == test_attachment.mime
assert element_dict["url"] == test_attachment.url
assert element_dict["objectKey"] == test_attachment.object_key
assert test_attachment.metadata
assert element_dict["display"] == test_attachment.metadata["display"]
assert element_dict["language"] == test_attachment.metadata["language"]
assert element_dict["type"] == test_attachment.metadata["type"]
async def test_upsert_feedback_create(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
):
feedback = Feedback(forId="test_step_id", value=1, comment="Great!")
mock_literal_client.api.create_score.return_value = Mock(id="new_feedback_id")
result = await literal_data_layer.upsert_feedback(feedback)
mock_literal_client.api.create_score.assert_awaited_once_with(
step_id="test_step_id",
value=1,
comment="Great!",
name="user-feedback",
type="HUMAN",
)
assert result == "new_feedback_id"
async def test_upsert_feedback_update(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
):
feedback = Feedback(
id="existing_feedback_id",
forId="test_step_id",
value=0,
comment="Needs improvement",
)
result = await literal_data_layer.upsert_feedback(feedback)
mock_literal_client.api.update_score.assert_awaited_once_with(
id="existing_feedback_id",
update_params={
"comment": "Needs improvement",
"value": 0,
},
)
assert result == "existing_feedback_id"
async def test_delete_feedback(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
):
feedback_id = "test_feedback_id"
result = await literal_data_layer.delete_feedback(feedback_id)
mock_literal_client.api.delete_score.assert_awaited_once_with(id=feedback_id)
assert result is True
async def test_delete_feedback_empty_id(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
):
feedback_id = ""
result = await literal_data_layer.delete_feedback(feedback_id)
mock_literal_client.api.delete_score.assert_not_awaited()
assert result is False
async def test_build_debug_url(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
):
mock_literal_client.api.get_my_project_id.return_value = "test_project_id"
mock_literal_client.api.url = "https://api.example.com"
result = await literal_data_layer.build_debug_url()
mock_literal_client.api.get_my_project_id.assert_awaited_once()
assert (
result
== "https://api.example.com/projects/test_project_id/logs/threads/[thread_id]?currentStepId=[step_id]"
)
async def test_build_debug_url_error(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
caplog,
):
mock_literal_client.api.get_my_project_id.side_effect = Exception("API Error")
result = await literal_data_layer.build_debug_url()
mock_literal_client.api.get_my_project_id.assert_awaited_once()
assert result == ""
assert "Error building debug url: API Error" in caplog.text
async def test_delete_element(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
mock_chainlit_context,
):
element_id = "test_element_id"
async with mock_chainlit_context:
await literal_data_layer.delete_element(element_id)
mock_literal_client.api.delete_attachment.assert_awaited_once_with(id=element_id)
async def test_delete_step(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
mock_chainlit_context,
):
step_id = "test_step_id"
async with mock_chainlit_context:
await literal_data_layer.delete_step(step_id)
mock_literal_client.api.delete_step.assert_awaited_once_with(id=step_id)
async def test_update_step(
literal_data_layer: LiteralDataLayer,
mock_literal_client: Mock,
mock_chainlit_context,
test_step_dict: StepDict,
):
async with mock_chainlit_context:
await literal_data_layer.update_step(test_step_dict)
mock_literal_client.api.send_steps.assert_awaited_once_with(
[
{
"createdAt": "2023-01-01T00:00:00Z",
"startTime": "2023-01-01T00:00:00Z",
"endTime": "2023-01-01T00:00:00Z",
"generation": {},
"id": "test_step_id",
"parentId": None,
"name": "Test Step",
"threadId": "test_thread_id",
"type": "user_message",
"tags": [],
"metadata": {
"key": "value",
"waitForAnswer": True,
"language": "en",
"showInput": True,
},
"input": {"content": "test input"},
"output": {"content": "test output"},
}
]
)
def test_steptype_to_steptype():
assert (
LiteralToChainlitConverter.steptype_to_steptype("user_message")
== "user_message"
)
assert (
LiteralToChainlitConverter.steptype_to_steptype("assistant_message")
== "assistant_message"
)
assert (
LiteralToChainlitConverter.steptype_to_steptype("system_message")
== "system_message"
)
assert LiteralToChainlitConverter.steptype_to_steptype("tool") == "tool"
assert LiteralToChainlitConverter.steptype_to_steptype(None) == "undefined"
def test_score_to_feedbackdict():
score = LiteralScore(
id="test_score_id",
step_id="test_step_id",
value=1,
comment="Great job!",
name="user-feedback",
type="HUMAN",
dataset_experiment_item_id=None,
tags=None,
)
feedback_dict = LiteralToChainlitConverter.score_to_feedbackdict(score)
assert feedback_dict == {
"id": "test_score_id",
"forId": "test_step_id",
"value": 1,
"comment": "Great job!",
}
assert LiteralToChainlitConverter.score_to_feedbackdict(None) is None
score.value = 0
feedback_dict = LiteralToChainlitConverter.score_to_feedbackdict(score)
assert feedback_dict is not None
assert feedback_dict["value"] == 0
score.id = None
score.step_id = None
feedback_dict = LiteralToChainlitConverter.score_to_feedbackdict(score)
assert feedback_dict is not None
assert feedback_dict["id"] == ""
assert feedback_dict["forId"] == ""
def test_step_to_stepdict():
literal_step = LiteralStep.from_dict(
{
"id": "test_step_id",
"threadId": "test_thread_id",
"type": "user_message",
"name": "Test Step",
"input": {"content": "test input"},
"output": {"content": "test output"},
"startTime": "2023-01-01T00:00:00Z",
"endTime": "2023-01-01T00:00:01Z",
"createdAt": "2023-01-01T00:00:00Z",
"metadata": {"showInput": True, "language": "en"},
"error": None,
"scores": [
{
"id": "test_score_id",
"stepId": "test_step_id",
"value": 1,
"comment": "Great job!",
"name": "user-feedback",
"type": "HUMAN",
}
],
}
)
step_dict = LiteralToChainlitConverter.step_to_stepdict(literal_step)
assert step_dict.get("id") == "test_step_id"
assert step_dict.get("threadId") == "test_thread_id"
assert step_dict.get("type") == "user_message"
assert step_dict.get("name") == "Test Step"
assert step_dict.get("input") == "test input"
assert step_dict.get("output") == "test output"
assert step_dict.get("start") == "2023-01-01T00:00:00Z"
assert step_dict.get("end") == "2023-01-01T00:00:01Z"
assert step_dict.get("createdAt") == "2023-01-01T00:00:00Z"
assert step_dict.get("showInput") == True
assert step_dict.get("language") == "en"
assert step_dict.get("isError") == False
assert step_dict.get("feedback") == {
"id": "test_score_id",
"forId": "test_step_id",
"value": 1,
"comment": "Great job!",
}
def test_attachment_to_elementdict():
attachment = Attachment(
id="test_attachment_id",
step_id="test_step_id",
thread_id="test_thread_id",
name="test.txt",
mime="text/plain",
url="https://example.com/test.txt",
object_key="test_object_key",
metadata={
"display": "side",
"language": "python",
"type": "file",
"size": "large",
},
)
element_dict = LiteralToChainlitConverter.attachment_to_elementdict(attachment)
assert element_dict["id"] == "test_attachment_id"
assert element_dict["forId"] == "test_step_id"
assert element_dict["threadId"] == "test_thread_id"
assert element_dict["name"] == "test.txt"
assert element_dict["mime"] == "text/plain"
assert element_dict["url"] == "https://example.com/test.txt"
assert element_dict["objectKey"] == "test_object_key"
assert element_dict["display"] == "side"
assert element_dict["language"] == "python"
assert element_dict["type"] == "file"
assert element_dict["size"] == "large"
def test_attachment_to_element():
attachment = Attachment(
id="test_attachment_id",
step_id="test_step_id",
thread_id="test_thread_id",
name="test.txt",
mime="text/plain",
url="https://example.com/test.txt",
object_key="test_object_key",
metadata={
"display": "side",
"language": "python",
"type": "text",
"size": "small",
},
)
element = LiteralToChainlitConverter.attachment_to_element(attachment)
assert isinstance(element, Text)
assert element.id == "test_attachment_id"
assert element.for_id == "test_step_id"
assert element.thread_id == "test_thread_id"
assert element.name == "test.txt"
assert element.mime == "text/plain"
assert element.url == "https://example.com/test.txt"
assert element.object_key == "test_object_key"
assert element.display == "side"
assert element.language == "python"
assert element.size == "small"
# Test other element types
for element_type in ["file", "image", "audio", "video", "pdf"]:
attachment.metadata = {"type": element_type, "size": "small"}
element = LiteralToChainlitConverter.attachment_to_element(attachment)
assert isinstance(
element,
{
"file": File,
"image": Image,
"audio": Audio,
"video": Video,
"text": Text,
"pdf": Pdf,
}[element_type],
)
def test_step_to_step():
literal_step = LiteralStep.from_dict(
{
"id": "test_step_id",
"threadId": "test_thread_id",
"type": "user_message",
"name": "Test Step",
"input": {"content": "test input"},
"output": {"content": "test output"},
"startTime": "2023-01-01T00:00:00Z",
"endTime": "2023-01-01T00:00:01Z",
"createdAt": "2023-01-01T00:00:00Z",
"metadata": {"showInput": True, "language": "en"},
"error": None,
"attachments": [
{
"id": "test_attachment_id",
"name": "test.txt",
"mime": "text/plain",
"url": "https://example.com/test.txt",
"objectKey": "test_object_key",
"metadata": {
"display": "side",
"language": "python",
"type": "text",
},
}
],
}
)
chainlit_step = LiteralToChainlitConverter.step_to_step(literal_step)
assert isinstance(chainlit_step, Step)
assert chainlit_step.id == "test_step_id"
assert chainlit_step.thread_id == "test_thread_id"
assert chainlit_step.type == "user_message"
assert chainlit_step.name == "Test Step"
assert chainlit_step.input == "test input"
assert chainlit_step.output == "test output"
assert chainlit_step.start == "2023-01-01T00:00:00Z"
assert chainlit_step.end == "2023-01-01T00:00:01Z"
assert chainlit_step.created_at == "2023-01-01T00:00:00Z"
assert chainlit_step.metadata == {"showInput": True, "language": "en"}
assert not chainlit_step.is_error
assert chainlit_step.elements is not None
assert len(chainlit_step.elements) == 1
assert isinstance(chainlit_step.elements[0], Text)
def test_thread_to_threaddict():
attachment_dict = LiteralAttachmentDict(
id="test_attachment_id",
stepId="test_step_id",
threadId="test_thread_id",
name="test.txt",
mime="text/plain",
url="https://example.com/test.txt",
objectKey="test_object_key",
metadata={
"display": "side",
"language": "python",
"type": "text",
},
)
step_dict = LiteralStepDict(
id="test_step_id",
threadId="test_thread_id",
type="user_message",
name="Test Step",
input={"content": "test input"},
output={"content": "test output"},
startTime="2023-01-01T00:00:00Z",
endTime="2023-01-01T00:00:01Z",
createdAt="2023-01-01T00:00:00Z",
metadata={"showInput": True, "language": "en"},
error=None,
attachments=[attachment_dict],
)
literal_thread = LiteralThread.from_dict(
LiteralThreadDict(
id="test_thread_id",
name="Test Thread",
createdAt="2023-01-01T00:00:00Z",
participant=UserDict(id="test_user_id", identifier="test_user_identifier_"),
tags=["tag1", "tag2"],
metadata={"key": "value"},
steps=[step_dict],
)
)
thread_dict = LiteralToChainlitConverter.thread_to_threaddict(literal_thread)
assert thread_dict["id"] == "test_thread_id"
assert thread_dict["name"] == "Test Thread"
assert thread_dict["createdAt"] == "2023-01-01T00:00:00Z"
assert thread_dict["userId"] == "test_user_id"
assert thread_dict["userIdentifier"] == "test_user_identifier_"
assert thread_dict["tags"] == ["tag1", "tag2"]
assert thread_dict["metadata"] == {"key": "value"}
assert thread_dict["steps"] is not None
assert len(thread_dict["steps"]) == 1
assert thread_dict["elements"] is not None
assert len(thread_dict["elements"]) == 1