Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/client/acontext-py/src/acontext/resources/async_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
Message,
MessageObservingStatus,
Session,
SessionSearchResult,
TokenCounts,
)
from ..uploads import FileUpload, normalize_file_upload
Expand Down Expand Up @@ -502,3 +503,31 @@ async def patch_configs(
json_data=payload,
)
return data.get("configs", {}) # type: ignore

async def search(
self,
*,
query: str,
user_id: str,
limit: int | None = None,
) -> SessionSearchResult:
"""Search for sessions by semantic similarity to a query string.

Args:
query: The search query text.
user_id: The User ID to search within.
limit: Maximum number of results to return (1-100, default 10).

Returns:
SessionSearchResult containing list of matching session UUIDs.

Example:
>>> result = await client.sessions.search(query="conversations", user_id="user_123")
>>> for session_id in result.session_ids:
... print(session_id)
"""
params = build_params(query=query, user_id=user_id, limit=limit)
data = await self._requester.request(
"GET", "/sessions/search", params=params or None
)
return SessionSearchResult.model_validate(data)
33 changes: 33 additions & 0 deletions src/client/acontext-py/src/acontext/resources/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
Message,
MessageObservingStatus,
Session,
SessionSearchResult,
TokenCounts,
)
from ..uploads import FileUpload, normalize_file_upload
Expand Down Expand Up @@ -496,3 +497,35 @@ def patch_configs(
json_data=payload,
)
return data.get("configs", {}) # type: ignore

def search(
self,
*,
query: str,
user_id: str,
project_id: str,
limit: int | None = None,
) -> SessionSearchResult:
"""Search for sessions by semantic similarity to a query string.

Args:
query: The search query text.
user_id: The User ID to search within.
project_id: The Project ID to search within.
limit: Maximum number of results to return (1-100, default 10).

Returns:
SessionSearchResult containing list of matching session UUIDs.

Example:
>>> result = client.sessions.search(
... query="conversations about authentication",
... user_id="user_123",
... project_id="proj_456"
... )
>>> for session_id in result.session_ids:
... print(session_id)
"""
params = build_params(query=query, user_id=user_id, project_id=project_id, limit=limit)
data = self._requester.request("GET", "/sessions/search", params=params or None)
return SessionSearchResult.model_validate(data)
6 changes: 6 additions & 0 deletions src/client/acontext-py/src/acontext/types/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,9 @@ class MessageObservingStatus(BaseModel):
)
pending: int = Field(..., description="Number of messages with pending status")
updated_at: str = Field(..., description="Timestamp when the status was retrieved")


class SessionSearchResult(BaseModel):
"""Response model for session search."""

session_ids: list[str] = Field(..., description="List of matching session UUIDs")
36 changes: 36 additions & 0 deletions src/client/acontext-ts/src/resources/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,4 +438,40 @@ export class SessionsAPI {
});
return (data as { configs: Record<string, unknown> }).configs ?? {};
}

/**
* Search for sessions by semantic similarity to a query string.
*
* @param options - Options for searching sessions.
* @param options.query - The search query text.
* @param options.userId - The User ID to search within.
* @param options.projectId - The Project ID to search within.
* @param options.limit - Maximum number of results to return (1-100, default 10).
* @returns SessionSearchResult containing list of matching session UUIDs.
*
* @example
* const result = await client.sessions.search({
* query: 'conversations about authentication',
* userId: 'user_123',
* projectId: 'proj_456'
* });
*/
async search(options: {
query: string;
userId: string;
projectId: string;
limit?: number | null;
}): Promise<{ session_ids: string[] }> {
const params = buildParams({
query: options.query,
user_id: options.userId,
project_id: options.projectId,
limit: options.limit ?? null,
});
const data = await this.requester.request('GET', '/sessions/search', {
params: Object.keys(params).length > 0 ? params : undefined,
});
// Assuming SessionSearchResult schema exists or returning generic object
return data as { session_ids: string[] };
}
}
3 changes: 3 additions & 0 deletions src/server/api/go/configs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ telemetry:

artifact:
maxUploadSizeBytes: ${ARTIFACT_MAX_UPLOAD_SIZE_BYTES} # Default 16MB (16 * 1024 * 1024 bytes)

embedding:
taskVectorDim: ${TASK_VECTOR_DIMENSION}
1 change: 1 addition & 0 deletions src/server/api/go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/go-playground/validator/v10 v10.30.1
github.com/google/uuid v1.6.0
github.com/openai/openai-go/v3 v3.22.0
github.com/pgvector/pgvector-go v0.2.2
github.com/rabbitmq/amqp091-go v1.10.0
github.com/redis/go-redis/extra/redisotel/v9 v9.18.0
github.com/redis/go-redis/v9 v9.18.0
Expand Down
30 changes: 30 additions & 0 deletions src/server/api/go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs=
cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
entgo.io/ent v0.13.1 h1:uD8QwN1h6SNphdCCzmkMN3feSUzNnVvV/WIkHKMbzOE=
entgo.io/ent v0.13.1/go.mod h1:qCEmo+biw3ccBn9OyL4ZK5dfpwg++l1Gxwac5B1206A=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/ClickHouse/ch-go v0.70.0 h1:/0lJpiSXxg/7IaJi7TOkKAOHrx0z0OiSMU475EJNAwM=
Expand Down Expand Up @@ -139,6 +141,10 @@ github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxE
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
github.com/go-pg/pg/v10 v10.11.0 h1:CMKJqLgTrfpE/aOVeLdybezR2om071Vh38OLZjsyMI0=
github.com/go-pg/pg/v10 v10.11.0/go.mod h1:4BpHRoxE61y4Onpof3x1a2SQvi9c+q1dJnrNdMjsroA=
github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU=
github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
Expand Down Expand Up @@ -195,6 +201,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
Expand All @@ -213,6 +221,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
Expand All @@ -232,6 +242,8 @@ github.com/paulmach/orb v0.12.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/En
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pgvector/pgvector-go v0.2.2 h1:Q/oArmzgbEcio88q0tWQksv/u9Gnb1c3F1K2TnalxR0=
github.com/pgvector/pgvector-go v0.2.2/go.mod h1:u5sg3z9bnqVEdpe1pkTij8/rFhTaMCMNyQagPDLK8gQ=
github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0=
github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down Expand Up @@ -303,10 +315,26 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tiktoken-go/tokenizer v0.7.0 h1:VMu6MPT0bXFDHr7UPh9uii7CNItVt3X9K90omxL54vw=
github.com/tiktoken-go/tokenizer v0.7.0/go.mod h1:6UCYI/DtOallbmL7sSy30p6YQv60qNyU/4aVigPOx6w=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/uptrace/bun v1.1.12 h1:sOjDVHxNTuM6dNGaba0wUuz7KvDE1BmNu9Gqs2gJSXQ=
github.com/uptrace/bun v1.1.12/go.mod h1:NPG6JGULBeQ9IU6yHp7YGELRa5Agmd7ATZdz4tGZ6z0=
github.com/uptrace/bun/dialect/pgdialect v1.1.12 h1:m/CM1UfOkoBTglGO5CUTKnIKKOApOYxkcP2qn0F9tJk=
github.com/uptrace/bun/dialect/pgdialect v1.1.12/go.mod h1:Ij6WIxQILxLlL2frUBxUBOZJtLElD2QQNDcu/PWDHTc=
github.com/uptrace/bun/driver/pgdriver v1.1.12 h1:3rRWB1GK0psTJrHwxzNfEij2MLibggiLdTqjTtfHc1w=
github.com/uptrace/bun/driver/pgdriver v1.1.12/go.mod h1:ssYUP+qwSEgeDDS1xm2XBip9el1y9Mi5mTAvLoiADLM=
github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
Expand Down Expand Up @@ -460,3 +488,5 @@ gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
gorm.io/plugin/opentelemetry v0.1.16 h1:Kypj2YYAliJqkIczDZDde6P6sFMhKSlG5IpngMFQGpc=
gorm.io/plugin/opentelemetry v0.1.16/go.mod h1:P3RmTeZXT+9n0F1ccUqR5uuTvEXDxF8k2UpO7mTIB2Y=
mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
17 changes: 17 additions & 0 deletions src/server/api/go/internal/bootstrap/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bootstrap
import (
"context"
"crypto/tls"
"fmt"
"strings"
"time"

Expand Down Expand Up @@ -53,6 +54,9 @@ func BuildContainer() *do.Injector {
// ALTER TABLE agent_skills DROP COLUMN IF EXISTS asset_meta;
// ALTER TABLE agent_skills DROP COLUMN IF EXISTS file_index;
if cfg.Database.AutoMigrate {
// Ensure pgvector extension exists
_ = d.Exec("CREATE EXTENSION IF NOT EXISTS vector")

_ = d.AutoMigrate(
&model.Project{},
&model.User{},
Expand All @@ -66,6 +70,19 @@ func BuildContainer() *do.Injector {
&model.AgentSkills{},
&model.SandboxLog{},
)

// Create the tasks.embedding vector column with the configured dimension.
dim := cfg.Embedding.TaskVectorDim
_ = d.Exec(fmt.Sprintf(
`DO $$ BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name='tasks' AND column_name='embedding'
) THEN
ALTER TABLE tasks ADD COLUMN embedding vector(%d);
END IF;
END $$;`, dim,
))
}

// ensure default project exists
Expand Down
6 changes: 6 additions & 0 deletions src/server/api/go/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ type ArtifactCfg struct {
MaxUploadSizeBytes int64 // Maximum file upload size in bytes
}

type EmbeddingCfg struct {
TaskVectorDim int
}

type Config struct {
App AppCfg
Root RootCfg
Expand All @@ -97,6 +101,7 @@ type Config struct {
Core CoreCfg
Telemetry TelemetryCfg
Artifact ArtifactCfg
Embedding EmbeddingCfg
}

func setDefaults(v *viper.Viper) {
Expand Down Expand Up @@ -127,6 +132,7 @@ func setDefaults(v *viper.Viper) {
v.SetDefault("telemetry.enabled", true)
v.SetDefault("telemetry.sampleRatio", 1.0) // Default 100% sampling
v.SetDefault("artifact.maxUploadSizeBytes", 16777216) // Default 16MB (16 * 1024 * 1024 bytes)
v.SetDefault("embedding.taskVectorDim", 1536)
}

func Load() (*Config, error) {
Expand Down
48 changes: 48 additions & 0 deletions src/server/api/go/internal/infra/httpclient/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,3 +394,51 @@ func (c *CoreClient) UploadSandboxFile(ctx context.Context, projectID, sandboxID

return &result, nil
}

// SessionSearchResponse represents the response from session search
type SessionSearchResponse struct {
SessionIDs []uuid.UUID `json:"session_ids"`
}

// SessionSearch calls the session search endpoint in Python Core
func (c *CoreClient) SessionSearch(ctx context.Context, userID string, projectID string, query string, limit int) (*SessionSearchResponse, error) {
endpoint := fmt.Sprintf("%s/api/v1/sessions/search", c.BaseURL)

httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
}

// Add query parameters
q := httpReq.URL.Query()
q.Add("user_id", userID)
q.Add("project_id", projectID)
q.Add("query", query)
q.Add("limit", fmt.Sprintf("%d", limit))
httpReq.URL.RawQuery = q.Encode()

resp, err := c.HTTPClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("do request: %w", err)
}
defer resp.Body.Close()

respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read response body: %w", err)
}

if resp.StatusCode != http.StatusOK {
c.Logger.Error("session_search request failed",
zap.Int("status_code", resp.StatusCode),
zap.String("body", string(respBody)))
return nil, fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(respBody))
}

var result SessionSearchResponse
if err := sonic.Unmarshal(respBody, &result); err != nil {
return nil, fmt.Errorf("unmarshal response: %w", err)
}

return &result, nil
}
42 changes: 42 additions & 0 deletions src/server/api/go/internal/modules/handler/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,3 +778,45 @@ func (h *SessionHandler) PatchConfigs(c *gin.Context) {

c.JSON(http.StatusOK, serializer.Response{Data: PatchSessionConfigsResp{Configs: updatedConfigs}})
}

type SessionSearchReq struct {
Query string `form:"query" json:"query" binding:"required" example:"find conversations about authentication"`
UserID string `form:"user_id" json:"user_id" binding:"required" example:"123e4567-e89b-12d3-a456-426614174000"`
ProjectID string `form:"project_id" json:"project_id" binding:"required" example:"123e4567-e89b-12d3-a456-426614174001"`
Limit int `form:"limit,default=10" json:"limit" binding:"omitempty,min=1,max=100" example:"10"`
}

// SessionSearch godoc
//
// @Summary Search sessions
// @Description Search for sessions by semantic similarity to a query string
// @Tags session
// @Accept json
// @Produce json
// @Param query query string true "Search query text"
// @Param user_id query string true "User ID"
// @Param project_id query string true "Project ID"
// @Param limit query integer false "Maximum number of results (1-100, default 10)"
// @Security BearerAuth
// @Success 200 {object} serializer.Response{data=httpclient.SessionSearchResponse}
// @Router /sessions/search [get]
func (h *SessionHandler) SessionSearch(c *gin.Context) {
req := SessionSearchReq{}
if err := c.ShouldBind(&req); err != nil {
c.JSON(http.StatusBadRequest, serializer.ParamErr("", err))
return
}

limit := req.Limit
if limit == 0 {
limit = 10
}

result, err := h.coreClient.SessionSearch(c.Request.Context(), req.UserID, req.ProjectID, req.Query, limit)
if err != nil {
c.JSON(http.StatusInternalServerError, serializer.Err(http.StatusInternalServerError, "failed to search sessions", err))
return
}

c.JSON(http.StatusOK, serializer.Response{Data: result})
}
Loading