fix agnts

This commit is contained in:
Nicolò Boschi 2025-11-13 14:50:51 +01:00
parent 3f22dfcad4
commit f521b36f9f
8 changed files with 109 additions and 201 deletions

View file

@ -31,3 +31,38 @@ export async function GET(request: NextRequest) {
);
}
}
export async function DELETE(request: NextRequest) {
try {
const searchParams = request.nextUrl.searchParams;
const agentId = searchParams.get('agent_id');
const unitId = searchParams.get('unit_id');
if (!agentId) {
return NextResponse.json(
{ error: 'agent_id is required' },
{ status: 400 }
);
}
if (!unitId) {
return NextResponse.json(
{ error: 'unit_id is required' },
{ status: 400 }
);
}
const response = await fetch(
`${DATAPLANE_URL}/api/v1/agents/${agentId}/memories/${unitId}`,
{ method: 'DELETE' }
);
const data = await response.json();
return NextResponse.json(data, { status: response.status });
} catch (error) {
console.error('Error deleting memory unit:', error);
return NextResponse.json(
{ error: 'Failed to delete memory unit' },
{ status: 500 }
);
}
}

View file

@ -19,3 +19,34 @@ export async function GET(
);
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ agentId: string }> }
) {
try {
const { agentId } = await params;
const searchParams = request.nextUrl.searchParams;
const operationId = searchParams.get('operation_id');
if (!operationId) {
return NextResponse.json(
{ error: 'operation_id is required' },
{ status: 400 }
);
}
const response = await fetch(
`${DATAPLANE_URL}/api/v1/agents/${agentId}/operations/${operationId}`,
{ method: 'DELETE' }
);
const data = await response.json();
return NextResponse.json(data, { status: response.status });
} catch (error) {
console.error('Error canceling operation:', error);
return NextResponse.json(
{ error: 'Failed to cancel operation' },
{ status: 500 }
);
}
}

View file

@ -19,7 +19,9 @@ export function AgentProvider({ children }: { children: React.ReactNode }) {
const loadAgents = async () => {
try {
const data = await dataplaneClient.listAgents();
setAgents(data.agents);
// Extract agent_id from each agent object
const agentIds = data.agents.map((agent: any) => agent.agent_id);
setAgents(agentIds);
} catch (error) {
console.error('Error loading agents:', error);
}

View file

@ -36,10 +36,9 @@ export class DataplaneClient {
reranker?: string;
trace?: boolean;
}) {
const { agent_id, ...body } = params;
return this.fetchApi(`/api/v1/agents/${agent_id}/memories/search`, {
return this.fetchApi(`/api/search`, {
method: 'POST',
body: JSON.stringify(body),
body: JSON.stringify(params),
});
}
@ -51,10 +50,9 @@ export class DataplaneClient {
agent_id: string;
thinking_budget?: number;
}) {
const { agent_id, ...body } = params;
return this.fetchApi(`/api/v1/agents/${agent_id}/think`, {
return this.fetchApi(`/api/think`, {
method: 'POST',
body: JSON.stringify(body),
body: JSON.stringify(params),
});
}
@ -70,10 +68,9 @@ export class DataplaneClient {
}>;
document_id?: string;
}) {
const { agent_id, ...body } = params;
return this.fetchApi(`/api/v1/agents/${agent_id}/memories`, {
return this.fetchApi(`/api/memories/batch`, {
method: 'POST',
body: JSON.stringify(body),
body: JSON.stringify(params),
});
}
@ -90,10 +87,9 @@ export class DataplaneClient {
}>;
document_id?: string;
}) {
const { agent_id, ...body } = params;
return this.fetchApi(`/api/v1/agents/${agent_id}/memories/async`, {
return this.fetchApi(`/api/memories/batch_async`, {
method: 'POST',
body: JSON.stringify(body),
body: JSON.stringify(params),
});
}
@ -101,14 +97,14 @@ export class DataplaneClient {
* List all agents
*/
async listAgents() {
return this.fetchApi<{ agents: any[] }>('/api/v1/agents', { cache: 'no-store' });
return this.fetchApi<{ agents: any[] }>('/api/agents', { cache: 'no-store' });
}
/**
* Get agent statistics
*/
async getAgentStats(agentId: string) {
return this.fetchApi(`/api/v1/agents/${agentId}/stats`);
return this.fetchApi(`/api/stats/${agentId}`);
}
/**
@ -119,10 +115,10 @@ export class DataplaneClient {
fact_type?: string;
}) {
const queryParams = new URLSearchParams();
queryParams.append('agent_id', params.agent_id);
if (params.fact_type) queryParams.append('fact_type', params.fact_type);
const path = `/api/v1/agents/${params.agent_id}/graph${queryParams.toString() ? `?${queryParams}` : ''}`;
return this.fetchApi(path);
return this.fetchApi(`/api/graph?${queryParams}`);
}
/**
@ -136,13 +132,13 @@ export class DataplaneClient {
offset?: number;
}) {
const queryParams = new URLSearchParams();
queryParams.append('agent_id', params.agent_id);
if (params.fact_type) queryParams.append('fact_type', params.fact_type);
if (params.q) queryParams.append('q', params.q);
if (params.limit) queryParams.append('limit', params.limit.toString());
if (params.offset) queryParams.append('offset', params.offset.toString());
const path = `/api/v1/agents/${params.agent_id}/memories/list${queryParams.toString() ? `?${queryParams}` : ''}`;
return this.fetchApi(path);
return this.fetchApi(`/api/list?${queryParams}`);
}
/**
@ -155,33 +151,33 @@ export class DataplaneClient {
offset?: number;
}) {
const queryParams = new URLSearchParams();
queryParams.append('agent_id', params.agent_id);
if (params.q) queryParams.append('q', params.q);
if (params.limit) queryParams.append('limit', params.limit.toString());
if (params.offset) queryParams.append('offset', params.offset.toString());
const path = `/api/v1/agents/${params.agent_id}/documents${queryParams.toString() ? `?${queryParams}` : ''}`;
return this.fetchApi(path);
return this.fetchApi(`/api/documents?${queryParams}`);
}
/**
* Get document by ID
*/
async getDocument(documentId: string, agentId: string) {
return this.fetchApi(`/api/v1/agents/${agentId}/documents/${documentId}`);
return this.fetchApi(`/api/documents/${documentId}?agent_id=${agentId}`);
}
/**
* List async operations for an agent
*/
async listOperations(agentId: string) {
return this.fetchApi(`/api/v1/agents/${agentId}/operations`);
return this.fetchApi(`/api/operations/${agentId}`);
}
/**
* Cancel a pending async operation
*/
async cancelOperation(agentId: string, operationId: string) {
return this.fetchApi(`/api/v1/agents/${agentId}/operations/${operationId}`, {
return this.fetchApi(`/api/operations/${agentId}?operation_id=${operationId}`, {
method: 'DELETE',
});
}
@ -190,7 +186,7 @@ export class DataplaneClient {
* Delete a memory unit
*/
async deleteMemoryUnit(agentId: string, unitId: string) {
return this.fetchApi(`/api/v1/agents/${agentId}/memories/${unitId}`, {
return this.fetchApi(`/api/list?agent_id=${agentId}&unit_id=${unitId}`, {
method: 'DELETE',
});
}

View file

@ -1,147 +0,0 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts.
# this is typically a path given in POSIX (e.g. forward slashes)
# format, relative to the token %(here)s which refers to the location of this
# ini file
script_location = %(here)s/alembic
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
# for all available tokens
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory. for multiple paths, the path separator
# is defined by "path_separator" below.
prepend_sys_path = .
# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the tzdata library which can be installed by adding
# `alembic[tz]` to the pip requirements.
# string value is passed to ZoneInfo()
# leave blank for localtime
# timezone =
# max length of characters to apply to the "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; This defaults
# to <script_location>/versions. When using multiple version
# directories, initial revisions must be specified with --version-path.
# The path separator used here should be the separator specified by "path_separator"
# below.
# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions
# path_separator; This indicates what character is used to split lists of file
# paths, including version_locations and prepend_sys_path within configparser
# files such as alembic.ini.
# The default rendered in new alembic.ini files is "os", which uses os.pathsep
# to provide os-dependent path splitting.
#
# Note that in order to support legacy alembic.ini files, this default does NOT
# take place if path_separator is not present in alembic.ini. If this
# option is omitted entirely, fallback logic is as follows:
#
# 1. Parsing of the version_locations option falls back to using the legacy
# "version_path_separator" key, which if absent then falls back to the legacy
# behavior of splitting on spaces and/or commas.
# 2. Parsing of the prepend_sys_path option falls back to the legacy
# behavior of splitting on spaces, commas, or colons.
#
# Valid values for path_separator are:
#
# path_separator = :
# path_separator = ;
# path_separator = space
# path_separator = newline
#
# Use os.pathsep. Default configuration used for new projects.
path_separator = os
# set to 'true' to search source files recursively
# in each "version_locations" directory
# new in Alembic version 1.10
# recursive_version_locations = false
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
# database URL. This is consumed by the user-maintained env.py script only.
# other means of configuring database URLs may be customized within the env.py
# file.
# sqlalchemy.url = driver://user:pass@localhost/dbname # Disabled, using DATABASE_URL from .env
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks = black
# black.type = console_scripts
# black.entrypoint = black
# black.options = -l 79 REVISION_SCRIPT_FILENAME
# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module
# hooks = ruff
# ruff.type = module
# ruff.module = ruff
# ruff.options = check --fix REVISION_SCRIPT_FILENAME
# Alternatively, use the exec runner to execute a binary found on your PATH
# hooks = ruff
# ruff.type = exec
# ruff.executable = ruff
# ruff.options = check --fix REVISION_SCRIPT_FILENAME
# Logging configuration. This is also consumed by the user-maintained
# env.py script only.
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARNING
handlers = console
qualname =
[logger_sqlalchemy]
level = WARNING
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

View file

@ -61,7 +61,6 @@ class SearchResult(BaseModel):
id: str
text: str
type: Optional[str] = None # fact type: world, agent, opinion
activation: Optional[float] = None
context: Optional[str] = None
event_date: Optional[str] = None # ISO format date string
@ -79,7 +78,6 @@ class SearchResponse(BaseModel):
"id": "123e4567-e89b-12d3-a456-426614174000",
"text": "Alice works at Google on the AI team",
"type": "world",
"activation": 0.95,
"context": "work info",
"event_date": "2024-01-15T10:30:00Z"
}
@ -201,7 +199,6 @@ class ThinkFact(BaseModel):
id: Optional[str] = None
text: str
type: Optional[str] = None # fact type: world, agent, opinion
activation: Optional[float] = None
context: Optional[str] = None
event_date: Optional[str] = None
@ -231,14 +228,12 @@ class ThinkResponse(BaseModel):
{
"id": "123",
"text": "AI is used in healthcare",
"type": "world",
"activation": 0.9
"type": "world"
},
{
"id": "456",
"text": "I discussed AI applications last week",
"type": "agent",
"activation": 0.85
"type": "agent"
}
],
"new_opinions": [

View file

@ -799,6 +799,21 @@ class TemporalSemanticMemory(
async with conn.transaction():
logger.debug("Inside transaction")
try:
# Ensure agent exists in agents table (create with defaults if not exists)
# Update updated_at to reflect recent activity
logger.debug(f"Ensuring agent '{agent_id}' exists in agents table")
await conn.execute(
"""
INSERT INTO agents (agent_id, personality, background)
VALUES ($1, $2::jsonb, $3)
ON CONFLICT (agent_id) DO UPDATE
SET updated_at = NOW()
""",
agent_id,
'{"openness": 0.5, "conscientiousness": 0.5, "extraversion": 0.5, "agreeableness": 0.5, "neuroticism": 0.5, "bias_strength": 0.5}',
""
)
# Handle document tracking with automatic upsert
if document_id:
logger.debug(f"Handling document tracking for {document_id}")
@ -1593,25 +1608,6 @@ class TemporalSemanticMemory(
except Exception as e:
raise Exception(f"Failed to delete agent data: {str(e)}")
async def list_agents(self) -> List[str]:
"""
Get list of all agent IDs in the database.
Returns:
List of agent IDs
"""
pool = await self._get_pool()
async with pool.acquire() as conn:
# Get distinct agent IDs from memory_units
agents = await conn.fetch("""
SELECT DISTINCT agent_id
FROM memory_units
WHERE agent_id IS NOT NULL
ORDER BY agent_id
""")
return [row['agent_id'] for row in agents]
async def get_graph_data(self, agent_id: Optional[str] = None, fact_type: Optional[str] = None):
"""
Get graph data for visualization.

View file

@ -248,7 +248,7 @@ wheels = [
[[package]]
name = "benchmarks"
version = "0.0.5"
version = "0.0.7"
source = { editable = "memora-dev/benchmarks" }
dependencies = [
{ name = "memora" },
@ -1408,7 +1408,7 @@ wheels = [
[[package]]
name = "memora"
version = "0.0.5"
version = "0.0.7"
source = { editable = "memora" }
dependencies = [
{ name = "alembic" },
@ -1464,7 +1464,7 @@ provides-extras = ["test"]
[[package]]
name = "memora-dev"
version = "0.0.5"
version = "0.0.7"
source = { editable = "memora-dev" }
dependencies = [
{ name = "memora" },