magickmind 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- magick_mind/__init__.py +39 -0
- magick_mind/auth/__init__.py +9 -0
- magick_mind/auth/base.py +46 -0
- magick_mind/auth/email_password.py +268 -0
- magick_mind/client.py +188 -0
- magick_mind/config.py +28 -0
- magick_mind/exceptions.py +107 -0
- magick_mind/http/__init__.py +5 -0
- magick_mind/http/client.py +313 -0
- magick_mind/models/__init__.py +17 -0
- magick_mind/models/auth.py +30 -0
- magick_mind/models/common.py +32 -0
- magick_mind/models/errors.py +73 -0
- magick_mind/models/v1/__init__.py +83 -0
- magick_mind/models/v1/api_keys.py +115 -0
- magick_mind/models/v1/artifact.py +151 -0
- magick_mind/models/v1/chat.py +104 -0
- magick_mind/models/v1/corpus.py +82 -0
- magick_mind/models/v1/end_user.py +75 -0
- magick_mind/models/v1/history.py +94 -0
- magick_mind/models/v1/mindspace.py +130 -0
- magick_mind/models/v1/model.py +25 -0
- magick_mind/models/v1/project.py +73 -0
- magick_mind/realtime/__init__.py +5 -0
- magick_mind/realtime/client.py +202 -0
- magick_mind/realtime/handler.py +122 -0
- magick_mind/resources/README.md +201 -0
- magick_mind/resources/__init__.py +42 -0
- magick_mind/resources/base.py +31 -0
- magick_mind/resources/v1/__init__.py +19 -0
- magick_mind/resources/v1/api_keys.py +181 -0
- magick_mind/resources/v1/artifact.py +287 -0
- magick_mind/resources/v1/chat.py +120 -0
- magick_mind/resources/v1/corpus.py +156 -0
- magick_mind/resources/v1/end_user.py +181 -0
- magick_mind/resources/v1/history.py +88 -0
- magick_mind/resources/v1/mindspace.py +331 -0
- magick_mind/resources/v1/model.py +19 -0
- magick_mind/resources/v1/project.py +155 -0
- magick_mind/routes.py +76 -0
- magickmind-0.1.1.dist-info/METADATA +593 -0
- magickmind-0.1.1.dist-info/RECORD +43 -0
- magickmind-0.1.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Artifact models for Magick Mind SDK v1 API.
|
|
3
|
+
|
|
4
|
+
Provides Pydantic models for file upload and artifact management using
|
|
5
|
+
presigned S3 URLs and webhook-based completion confirmation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Artifact(BaseModel):
|
|
16
|
+
"""
|
|
17
|
+
Canonical artifact model representing an uploaded file.
|
|
18
|
+
|
|
19
|
+
Artifacts are files (documents, images, etc.) uploaded to S3 and
|
|
20
|
+
associated with a corpus or other container in the bifrost backend.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
model_config = ConfigDict(
|
|
24
|
+
extra="allow"
|
|
25
|
+
) # Allow additional fields from API responses
|
|
26
|
+
|
|
27
|
+
id: str = Field(..., description="Unique artifact identifier")
|
|
28
|
+
bucket: str = Field(..., description="S3 bucket name")
|
|
29
|
+
key: str = Field(..., description="S3 object key")
|
|
30
|
+
s3_url: str = Field(..., description="S3 URL (s3://bucket/key)")
|
|
31
|
+
content_type: str = Field(..., description="MIME type of the artifact")
|
|
32
|
+
size_bytes: int = Field(..., description="Size in bytes")
|
|
33
|
+
etag: Optional[str] = Field(None, description="S3 ETag")
|
|
34
|
+
version_id: Optional[str] = Field(None, description="S3 version ID")
|
|
35
|
+
status: str = Field(
|
|
36
|
+
..., description="Artifact status (uploaded, processing, ready, failed)"
|
|
37
|
+
)
|
|
38
|
+
corpus_id: Optional[str] = Field(None, description="Associated corpus ID")
|
|
39
|
+
end_user_id: Optional[str] = Field(
|
|
40
|
+
None, description="End user who uploaded the artifact"
|
|
41
|
+
)
|
|
42
|
+
created_by: Optional[str] = Field(
|
|
43
|
+
None, description="Account ID that created the artifact"
|
|
44
|
+
)
|
|
45
|
+
created_at: Optional[int] = Field(
|
|
46
|
+
None, description="Creation timestamp (unix seconds)"
|
|
47
|
+
)
|
|
48
|
+
updated_at: Optional[int] = Field(
|
|
49
|
+
None, description="Last update timestamp (unix seconds)"
|
|
50
|
+
)
|
|
51
|
+
error_code: Optional[str] = Field(
|
|
52
|
+
None, description="Error code if status is failed"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class PresignArtifactRequest(BaseModel):
|
|
57
|
+
"""Request for obtaining a presigned S3 upload URL."""
|
|
58
|
+
|
|
59
|
+
file_name: str = Field(..., description="Name of the file to upload")
|
|
60
|
+
content_type: str = Field(..., description="MIME type of the file")
|
|
61
|
+
size_bytes: int = Field(..., description="File size in bytes", gt=0)
|
|
62
|
+
end_user_id: Optional[str] = Field(
|
|
63
|
+
None, description="End user identifier (optional)"
|
|
64
|
+
)
|
|
65
|
+
corpus_id: Optional[str] = Field(
|
|
66
|
+
None, description="Corpus to associate with (optional)"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class PresignArtifactResponse(BaseModel):
|
|
71
|
+
"""
|
|
72
|
+
Response containing presigned upload URL and metadata.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
success: Optional[bool] = Field(None, description="Request success status")
|
|
76
|
+
message: Optional[str] = Field(None, description="Response message")
|
|
77
|
+
id: Optional[str] = Field(None, description="Artifact ID (Relaxed)")
|
|
78
|
+
bucket: Optional[str] = Field(None, description="S3 bucket (Relaxed)")
|
|
79
|
+
key: Optional[str] = Field(None, description="S3 object key (Relaxed)")
|
|
80
|
+
s3_url: Optional[str] = Field(None, description="S3 URL (Relaxed)")
|
|
81
|
+
upload_url: Optional[str] = Field(None, description="Presigned PUT URL (Relaxed)")
|
|
82
|
+
expires_at: Optional[int] = Field(None, description="URL expiration time (Relaxed)")
|
|
83
|
+
required_headers: Optional[dict[str, str]] = Field(
|
|
84
|
+
None, description="HTTP headers (Relaxed)"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class GetArtifactResponse(BaseModel):
|
|
89
|
+
"""Response for getting a single artifact by ID."""
|
|
90
|
+
|
|
91
|
+
success: bool = Field(..., description="Request success status")
|
|
92
|
+
message: str = Field(..., description="Response message")
|
|
93
|
+
artifact: Artifact = Field(..., description="The artifact data")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ListArtifactsResponse(BaseModel):
|
|
97
|
+
"""Response for listing/querying artifacts."""
|
|
98
|
+
|
|
99
|
+
success: bool = Field(..., description="Request success status")
|
|
100
|
+
message: str = Field(..., description="Response message")
|
|
101
|
+
artifacts: list[Artifact] = Field(..., description="List of artifacts")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class DeleteArtifactResponse(BaseModel):
|
|
105
|
+
"""Response for deleting an artifact."""
|
|
106
|
+
|
|
107
|
+
success: bool = Field(..., description="Request success status")
|
|
108
|
+
message: str = Field(..., description="Response message")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class FinalizeArtifactRequest(BaseModel):
|
|
112
|
+
"""
|
|
113
|
+
Client-driven finalize request (fallback when webhook is unavailable).
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
artifact_id: str = Field(..., description="Artifact ID")
|
|
117
|
+
bucket: str = Field(..., description="S3 bucket")
|
|
118
|
+
key: str = Field(..., description="S3 object key")
|
|
119
|
+
version_id: Optional[str] = Field(None, description="S3 version ID")
|
|
120
|
+
size_bytes: Optional[int] = Field(None, description="Actual uploaded size")
|
|
121
|
+
content_type: Optional[str] = Field(None, description="Content type")
|
|
122
|
+
etag: Optional[str] = Field(None, description="S3 ETag")
|
|
123
|
+
checksum_sha256: Optional[str] = Field(None, description="SHA256 checksum")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class FinalizeArtifactResponse(BaseModel):
|
|
127
|
+
"""Response for finalize operation."""
|
|
128
|
+
|
|
129
|
+
success: Optional[bool] = Field(None, description="Request success status")
|
|
130
|
+
message: Optional[str] = Field(None, description="Response message")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class ArtifactWebhookPayload(BaseModel):
|
|
134
|
+
"""
|
|
135
|
+
Webhook payload sent from S3 Lambda or client finalize.
|
|
136
|
+
|
|
137
|
+
This is the shape of data sent to the webhook endpoint after
|
|
138
|
+
an artifact upload is complete.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
artifact_id: str = Field(..., description="Artifact ID")
|
|
142
|
+
corpus_id: Optional[str] = Field(None, description="Corpus ID if applicable")
|
|
143
|
+
bucket: str = Field(..., description="S3 bucket")
|
|
144
|
+
key: str = Field(..., description="S3 object key")
|
|
145
|
+
version_id: Optional[str] = Field(None, description="S3 version ID")
|
|
146
|
+
size_bytes: Optional[int] = Field(None, description="File size in bytes")
|
|
147
|
+
content_type: Optional[str] = Field(None, description="MIME type")
|
|
148
|
+
etag: Optional[str] = Field(None, description="S3 ETag")
|
|
149
|
+
checksum_sha256: Optional[str] = Field(None, description="SHA256 checksum")
|
|
150
|
+
status: str = Field(..., description="Status: uploaded, processing, ready, failed")
|
|
151
|
+
error_code: Optional[str] = Field(None, description="Error code if failed")
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""V1 chat API models.
|
|
2
|
+
|
|
3
|
+
These models mirror the bifrost API types for /v1/magickmind/chat endpoint.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field, field_serializer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ConfigSchema(BaseModel):
|
|
12
|
+
"""
|
|
13
|
+
Configuration for chat request.
|
|
14
|
+
|
|
15
|
+
Contains model selection and compute settings.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
fast_model_id: str = Field(..., description="Model ID for fast brain")
|
|
19
|
+
smart_model_ids: list[str] = Field(..., description="Model IDs for smart brain")
|
|
20
|
+
compute_power: int = Field(default=0, description="Compute power setting (0-100)")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ChatSendRequest(BaseModel):
|
|
24
|
+
"""
|
|
25
|
+
Request to send a chat message to a mindspace.
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
request = ChatSendRequest(
|
|
29
|
+
api_key="sk-...",
|
|
30
|
+
mindspace_id="mind-123",
|
|
31
|
+
message="Hello!",
|
|
32
|
+
enduser_id="user-456",
|
|
33
|
+
config=ConfigSchema(
|
|
34
|
+
fast_model_id="gpt-4",
|
|
35
|
+
smart_model_ids=["gpt-4"],
|
|
36
|
+
compute_power=50,
|
|
37
|
+
),
|
|
38
|
+
)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
api_key: str = Field(..., description="API key for LLM access")
|
|
42
|
+
mindspace_id: str = Field(..., description="Mindspace/chat conversation ID")
|
|
43
|
+
message: str = Field(..., description="User message text to send")
|
|
44
|
+
enduser_id: str = Field(..., description="End-user identifier")
|
|
45
|
+
config: ConfigSchema = Field(..., description="Model configuration")
|
|
46
|
+
reply_to_message_id: Optional[str] = Field(
|
|
47
|
+
default=None, description="ID of message being replied to"
|
|
48
|
+
)
|
|
49
|
+
additional_context: Optional[str] = Field(
|
|
50
|
+
default=None, description="Additional context for the message"
|
|
51
|
+
)
|
|
52
|
+
artifact_ids: Optional[list[str]] = Field(
|
|
53
|
+
default=None, description="List of artifact IDs to attach to message"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# =========================================================================
|
|
57
|
+
# Serializers: Transform None → default values for API contract compliance
|
|
58
|
+
# SDK users can omit these fields, but API expects them in the payload
|
|
59
|
+
# =========================================================================
|
|
60
|
+
|
|
61
|
+
@field_serializer("artifact_ids")
|
|
62
|
+
def serialize_artifact_ids(self, v: list[str] | None) -> list[str]:
|
|
63
|
+
"""API requires this field; default to empty list if not provided."""
|
|
64
|
+
return v or []
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ChatPayload(BaseModel):
|
|
68
|
+
"""
|
|
69
|
+
Chat response payload schema.
|
|
70
|
+
|
|
71
|
+
Flexible data container for chat response with message metadata.
|
|
72
|
+
Can be extended to support various response formats.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
message_id: Optional[str] = Field(
|
|
76
|
+
None, description="Generated message ID (Relaxed)"
|
|
77
|
+
)
|
|
78
|
+
task_id: Optional[str] = Field(None, description="Associated task ID (Relaxed)")
|
|
79
|
+
content: Optional[str] = Field(None, description="AI response text (Relaxed)")
|
|
80
|
+
reply_to: Optional[str] = Field(
|
|
81
|
+
default=None, description="ID of message being replied to"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ChatSendResponse(BaseModel):
|
|
86
|
+
"""
|
|
87
|
+
Response from sending a chat message.
|
|
88
|
+
|
|
89
|
+
Matches Bifrost ChatResponse structure with only content field.
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
{
|
|
93
|
+
"content": {
|
|
94
|
+
"message_id": "msg-789",
|
|
95
|
+
"task_id": "task-123",
|
|
96
|
+
"content": "Hello! How can I help you?",
|
|
97
|
+
"reply_to": null
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
content: Optional[ChatPayload] = Field(
|
|
103
|
+
default=None, description="Chat response payload"
|
|
104
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Corpus models for Magick Mind SDK v1 API.
|
|
3
|
+
|
|
4
|
+
Provides Pydantic models for corpus (knowledge base) management.
|
|
5
|
+
Corpus represents a collection of artifacts that can be used for
|
|
6
|
+
RAG (Retrieval Augmented Generation) workflows.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING, Optional
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from magick_mind.models.v1.end_user import PageInfo
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Corpus(BaseModel):
|
|
18
|
+
"""
|
|
19
|
+
Canonical corpus model representing a knowledge base.
|
|
20
|
+
|
|
21
|
+
A corpus is a collection of artifacts (documents, files) that can be
|
|
22
|
+
used for semantic search and retrieval augmented generation.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
26
|
+
|
|
27
|
+
id: str = Field(..., description="Unique corpus identifier")
|
|
28
|
+
name: str = Field(..., description="Corpus name")
|
|
29
|
+
description: Optional[str] = Field(None, description="Corpus description")
|
|
30
|
+
artifact_ids: Optional[list[str]] = Field(
|
|
31
|
+
None, description="List of artifact IDs in this corpus"
|
|
32
|
+
)
|
|
33
|
+
created_by: Optional[str] = Field(
|
|
34
|
+
None, description="Account ID that created the corpus"
|
|
35
|
+
)
|
|
36
|
+
created_at: Optional[str] = Field(
|
|
37
|
+
None, description="Creation timestamp (ISO format)"
|
|
38
|
+
)
|
|
39
|
+
updated_at: Optional[str] = Field(
|
|
40
|
+
None, description="Last update timestamp (ISO format)"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class CreateCorpusRequest(BaseModel):
|
|
45
|
+
"""Request for creating a new corpus."""
|
|
46
|
+
|
|
47
|
+
name: Optional[str] = Field(None, description="Corpus name (Relaxed)")
|
|
48
|
+
description: Optional[str] = Field(None, description="Corpus description (Relaxed)")
|
|
49
|
+
artifact_ids: Optional[list[str]] = Field(
|
|
50
|
+
None, description="Optional list of artifact IDs (Relaxed)"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ListCorpusResponse(BaseModel):
|
|
55
|
+
"""
|
|
56
|
+
Response for listing corpus.
|
|
57
|
+
|
|
58
|
+
Matches Bifrost's {data: list[Corpus], paging: PageInfo} structure.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
data: list[Corpus] = Field(..., description="List of corpus")
|
|
62
|
+
paging: "PageInfo" = Field(..., description="Pagination information")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class UpdateCorpusRequest(BaseModel):
|
|
66
|
+
"""Request for updating an existing corpus."""
|
|
67
|
+
|
|
68
|
+
name: Optional[str] = Field(None, description="Corpus name (Relaxed)")
|
|
69
|
+
description: Optional[str] = Field(None, description="Corpus description (Relaxed)")
|
|
70
|
+
artifact_ids: Optional[list[str]] = Field(
|
|
71
|
+
None, description="List of artifact IDs (Relaxed)"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class DeleteCorpusResponse:
|
|
76
|
+
"""
|
|
77
|
+
Response for deleting a corpus.
|
|
78
|
+
|
|
79
|
+
Bifrost returns 204 No Content with no response body.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
pass
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
End user models for Magick Mind SDK v1 API.
|
|
3
|
+
|
|
4
|
+
Mirrors Bifrost's /v1/end-users endpoint request/response schemas.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
|
|
11
|
+
from magick_mind.models.common import Cursors, PageInfo
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EndUser(BaseModel):
|
|
15
|
+
"""
|
|
16
|
+
End user schema from Bifrost.
|
|
17
|
+
|
|
18
|
+
Represents an end user in a multi-tenant agentic SaaS application.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
22
|
+
|
|
23
|
+
id: str = Field(..., description="End user ID")
|
|
24
|
+
name: str = Field(..., description="End user name")
|
|
25
|
+
external_id: Optional[str] = Field(
|
|
26
|
+
default=None,
|
|
27
|
+
description="Optional external ID for mapping to external systems",
|
|
28
|
+
)
|
|
29
|
+
tenant_id: str = Field(..., description="Tenant ID this end user belongs to")
|
|
30
|
+
created_by: Optional[str] = Field(default=None, description="User ID of creator")
|
|
31
|
+
updated_by: Optional[str] = Field(
|
|
32
|
+
default=None, description="User ID of last updater"
|
|
33
|
+
)
|
|
34
|
+
created_at: str = Field(..., description="Creation timestamp (ISO8601)")
|
|
35
|
+
updated_at: str = Field(..., description="Last update timestamp (ISO8601)")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CreateEndUserRequest(BaseModel):
|
|
39
|
+
"""
|
|
40
|
+
Request schema for creating a new end user.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
name: str = Field(..., description="End user name (required)")
|
|
44
|
+
external_id: Optional[str] = Field(
|
|
45
|
+
default=None,
|
|
46
|
+
description="Optional external ID for mapping to external systems",
|
|
47
|
+
)
|
|
48
|
+
tenant_id: Optional[str] = Field(None, description="Tenant ID (Relaxed)")
|
|
49
|
+
actor_id: Optional[str] = Field(None, description="Actor ID (Relaxed)")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class QueryEndUserResponse(BaseModel):
|
|
53
|
+
"""
|
|
54
|
+
Response schema for querying end users.
|
|
55
|
+
|
|
56
|
+
Uses new pagination pattern: {data: [], paging: {}}
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
data: list[EndUser] = Field(
|
|
60
|
+
default_factory=list, description="List of end users matching the query"
|
|
61
|
+
)
|
|
62
|
+
paging: PageInfo = Field(..., description="Pagination information")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class UpdateEndUserRequest(BaseModel):
|
|
66
|
+
"""
|
|
67
|
+
Request schema for updating an end user.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
name: Optional[str] = Field(default=None, description="End user name (optional)")
|
|
71
|
+
external_id: Optional[str] = Field(
|
|
72
|
+
default=None,
|
|
73
|
+
description="External ID for mapping to external systems (optional)",
|
|
74
|
+
)
|
|
75
|
+
tenant_id: Optional[str] = Field(None, description="Tenant ID (Relaxed)")
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
History models for Magick Mind SDK v1 API.
|
|
3
|
+
|
|
4
|
+
Mirrors Bifrost's /v1/mindspaces/messages endpoint response.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
|
|
11
|
+
from magick_mind.models.common import Cursors, PageInfo
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ChatHistoryMessage(BaseModel):
|
|
15
|
+
"""
|
|
16
|
+
Individual chat history message from Bifrost.
|
|
17
|
+
|
|
18
|
+
Maps to ChatHistoryItem from Bifrost's magickmind.api.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
22
|
+
|
|
23
|
+
id: Optional[str] = Field(None, description="Message ID")
|
|
24
|
+
mindspace_id: Optional[str] = Field(
|
|
25
|
+
None, description="Mindspace this message belongs to"
|
|
26
|
+
)
|
|
27
|
+
sent_by_user_id: Optional[str] = Field(
|
|
28
|
+
None, description="User who sent the message"
|
|
29
|
+
)
|
|
30
|
+
content: Optional[str] = Field(None, description="Message content/text")
|
|
31
|
+
reply_to_message_id: Optional[str] = Field(
|
|
32
|
+
default=None, description="ID of message being replied to"
|
|
33
|
+
)
|
|
34
|
+
status: Optional[str] = Field(None, description="Message status")
|
|
35
|
+
created_at: Optional[str] = Field(
|
|
36
|
+
None, alias="create_at", description="Creation timestamp (RFC3339)"
|
|
37
|
+
)
|
|
38
|
+
updated_at: Optional[str] = Field(
|
|
39
|
+
None, alias="update_at", description="Update timestamp (RFC3339)"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class HistoryResponse(BaseModel):
|
|
44
|
+
"""
|
|
45
|
+
Response from Bifrost's /v1/mindspaces/messages endpoint.
|
|
46
|
+
|
|
47
|
+
Uses standardized Bifrost pagination format:
|
|
48
|
+
{
|
|
49
|
+
"data": [...],
|
|
50
|
+
"paging": {
|
|
51
|
+
"cursors": {"after": "...", "before": "..."},
|
|
52
|
+
"has_more": true,
|
|
53
|
+
"has_previous": false
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
data: list[ChatHistoryMessage] = Field(
|
|
59
|
+
default_factory=list, description="List of chat messages"
|
|
60
|
+
)
|
|
61
|
+
paging: PageInfo = Field(
|
|
62
|
+
default_factory=lambda: PageInfo(
|
|
63
|
+
cursors=Cursors(after=None, before=None),
|
|
64
|
+
has_more=False,
|
|
65
|
+
has_previous=False,
|
|
66
|
+
),
|
|
67
|
+
description="Pagination information",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Computed convenience properties for backward compatibility
|
|
71
|
+
@property
|
|
72
|
+
def chat_histories(self) -> list[ChatHistoryMessage]:
|
|
73
|
+
"""Alias for data field (backward compatibility)."""
|
|
74
|
+
return self.data
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def has_more(self) -> bool:
|
|
78
|
+
"""True if more messages exist forward."""
|
|
79
|
+
return self.paging.has_more
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def has_older(self) -> bool:
|
|
83
|
+
"""True if more messages exist backward."""
|
|
84
|
+
return self.paging.has_previous
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def next_after_id(self) -> Optional[str]:
|
|
88
|
+
"""Cursor for forward pagination."""
|
|
89
|
+
return self.paging.cursors.after if self.paging.cursors else None
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def next_before_id(self) -> Optional[str]:
|
|
93
|
+
"""Cursor for backward pagination."""
|
|
94
|
+
return self.paging.cursors.before if self.paging.cursors else None
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""V1 mindspace API models.
|
|
2
|
+
|
|
3
|
+
These models mirror the bifrost API types for /v1/mindspaces endpoint.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Literal, Optional
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
from magick_mind.models.common import BaseResponse
|
|
13
|
+
from magick_mind.models.v1.history import HistoryResponse
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Type alias for mindspace type enum (uppercase to match apidog)
|
|
17
|
+
MindSpaceType = Literal["PRIVATE", "GROUP"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class MindSpace(BaseModel):
|
|
21
|
+
"""
|
|
22
|
+
Mindspace schema model.
|
|
23
|
+
|
|
24
|
+
Represents a mindspace container that can be private (single user)
|
|
25
|
+
or group (multiple users), with attached corpus for knowledge.
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
{
|
|
29
|
+
"id": "mind-123",
|
|
30
|
+
"name": "Engineering Team",
|
|
31
|
+
"description": "Team workspace",
|
|
32
|
+
"project_id": "proj-456",
|
|
33
|
+
"corpus_ids": ["corp-1", "corp-2"],
|
|
34
|
+
"user_ids": ["user-1", "user-2"],
|
|
35
|
+
"type": "GROUP",
|
|
36
|
+
"created_by": "user-1",
|
|
37
|
+
"updated_by": "user-1",
|
|
38
|
+
"created_at": "2025-12-16T09:00:00Z",
|
|
39
|
+
"updated_at": "2025-12-16T09:00:00Z"
|
|
40
|
+
}
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
id: str = Field(..., description="Mindspace ID")
|
|
44
|
+
name: str = Field(..., description="Mindspace name")
|
|
45
|
+
description: Optional[str] = Field(None, description="Mindspace description")
|
|
46
|
+
project_id: str = Field(..., description="Associated project ID")
|
|
47
|
+
corpus_ids: Optional[list[str]] = Field(
|
|
48
|
+
None,
|
|
49
|
+
description="List of corpus IDs attached to this mindspace",
|
|
50
|
+
)
|
|
51
|
+
user_ids: Optional[list[str]] = Field(
|
|
52
|
+
None,
|
|
53
|
+
description="List of user IDs with access to this mindspace",
|
|
54
|
+
)
|
|
55
|
+
type: MindSpaceType = Field(..., description="Mindspace type: 'PRIVATE' or 'GROUP'")
|
|
56
|
+
created_by: Optional[str] = Field(
|
|
57
|
+
None, description="User ID who created the mindspace"
|
|
58
|
+
)
|
|
59
|
+
updated_by: Optional[str] = Field(
|
|
60
|
+
None, description="User ID who last updated the mindspace"
|
|
61
|
+
)
|
|
62
|
+
created_at: Optional[str] = Field(None, description="Creation timestamp (RFC3339)")
|
|
63
|
+
updated_at: Optional[str] = Field(
|
|
64
|
+
None, description="Last update timestamp (RFC3339)"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class CreateMindSpaceRequest(BaseModel):
|
|
69
|
+
"""
|
|
70
|
+
Request to create a new mindspace.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
name: Optional[str] = Field(
|
|
74
|
+
None, description="Mindspace name (Relaxed)", max_length=100
|
|
75
|
+
)
|
|
76
|
+
type: Optional[MindSpaceType] = Field(None, description="Mindspace type (Relaxed)")
|
|
77
|
+
description: Optional[str] = Field(
|
|
78
|
+
default=None, description="Mindspace description", max_length=256
|
|
79
|
+
)
|
|
80
|
+
project_id: Optional[str] = Field(default=None, description="Associated project ID")
|
|
81
|
+
corpus_ids: list[str] = Field(
|
|
82
|
+
default_factory=list, description="List of corpus IDs to attach"
|
|
83
|
+
)
|
|
84
|
+
user_ids: list[str] = Field(
|
|
85
|
+
default_factory=list, description="List of user IDs to grant access"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class GetMindSpaceListResponse(BaseModel):
|
|
90
|
+
"""
|
|
91
|
+
Response from listing mindspaces.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
mindspaces: list[MindSpace] = Field(
|
|
95
|
+
default_factory=list, description="List of mindspaces"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class UpdateMindSpaceRequest(BaseModel):
|
|
100
|
+
"""
|
|
101
|
+
Request to update an existing mindspace.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
name: Optional[str] = Field(
|
|
105
|
+
None, description="Mindspace name (Relaxed)", max_length=100
|
|
106
|
+
)
|
|
107
|
+
description: Optional[str] = Field(
|
|
108
|
+
default=None, description="Mindspace description", max_length=256
|
|
109
|
+
)
|
|
110
|
+
project_id: Optional[str] = Field(default=None, description="Associated project ID")
|
|
111
|
+
corpus_ids: list[str] = Field(
|
|
112
|
+
default_factory=list, description="List of corpus IDs to attach"
|
|
113
|
+
)
|
|
114
|
+
user_ids: list[str] = Field(
|
|
115
|
+
default_factory=list, description="List of user IDs to grant access"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class AddMindSpaceUsersRequest(BaseModel):
|
|
120
|
+
"""
|
|
121
|
+
Request to add users to an existing mindspace.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
user_ids: list[str] = Field(
|
|
125
|
+
..., description="List of user IDs to add to the mindspace"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# Reuse HistoryResponse for messages endpoint since it's the same structure
|
|
130
|
+
MindspaceMessagesResponse = HistoryResponse
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Model(BaseModel):
|
|
6
|
+
"""Model information"""
|
|
7
|
+
|
|
8
|
+
id: Optional[str] = Field(None, description="The model identifier, e.g. 'gpt-4o'")
|
|
9
|
+
object: str = Field("model", description="The object type, always 'model'")
|
|
10
|
+
created: Optional[int] = Field(
|
|
11
|
+
None, description="The unix timestamp when the model was created"
|
|
12
|
+
)
|
|
13
|
+
owned_by: Optional[str] = Field(
|
|
14
|
+
None, description="The organization that owns the model"
|
|
15
|
+
)
|
|
16
|
+
provider: Optional[str] = Field(None, description="The provider of the model")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ModelsListResponse(BaseModel):
|
|
20
|
+
"""Response containing a list of models"""
|
|
21
|
+
|
|
22
|
+
object: str = Field("list", description="The object type, always 'list'")
|
|
23
|
+
data: List[Model] = Field(
|
|
24
|
+
default_factory=list, description="The list of available models"
|
|
25
|
+
)
|