Memória de longo prazo para agentes de IA.
O WOS é uma API de memória. Você armazena as memórias de um usuário uma única vez e depois recupera apenas as relevantes para cada consulta, passando-as ao prompt do seu modelo.
A recuperação é puramente semântica, sem correspondência por palavras-chave nem BM25, então a qualidade do recall é idêntica em todos os idiomas. Cada consulta retorna um contexto pequeno e limitado, não importa quanto você tenha armazenado, e nenhum modelo é executado sobre as suas memórias armazenadas.
Operações principais
store- salva uma memória para um usuário.recall- obtém as memórias relevantes para uma consulta. Esta é a chamada principal.search- busca semântica bruta sobre as memórias armazenadas.supersede- atualiza ou substitui uma memória desatualizada.forget- exclui uma única memória ou um usuário inteiro (GDPR).
Três modelos, uma mesma linhagem.
Os modelos WOS recebem nomes das formas como as pessoas preservaram o conhecimento ao longo da história - Tablet, Scroll, Book. Pedra, pergaminho, livro encadernado: cada um faz mais pelo seu agente do que o anterior.
Tablet
DisponívelUma forma enxuta, rápida e de baixo custo de inscrever e recuperar memória - a base sobre a qual todos os modelos são construídos.
Scroll
DisponívelAdiciona um modelo de linguagem para ler sua pergunta com mais atenção e trazer de volta um contexto mais completo, de modo que evidências dispersas voltem reunidas, em vez de faltar uma peça.
Book
Em breveAbre sozinho na página certa - escolhendo a memória e as ferramentas de que cada momento precisa, e ficando mais afiado quanto mais é usado.
O relatório completo de benchmark do Tablet 1 está na página de benchmarks.
Pague $2 para nós. Economize muitas vezes esse valor no seu LLM.
O WOS entrega ao seu LLM ~1,200 tokens por consulta - uma fatia limitada e relevante - em vez de enfiar o histórico completo em cada prompt. A diferença é enorme, e cresce junto com o seu histórico.
Cada $1 gasto no WOS economiza ~$98 no LLM. Histórico maior ou modelo mais caro → ROI maior.
De onde vem a economia
- Sem o WOS, você enfia o histórico inteiro em cada prompt -
100K tokens × $2.50/1M = $0.25por consulta, às tarifas de entrada do GPT-4o (cerca de 2× isso em modelos do nível do Opus). - Com o WOS, você ingere uma única vez (
$2/1M) e cada consulta passa a ser uma pequena recuperação ($3/1M × 1,200) mais o seu LLM sobre apenas ~1,200 tokens. - Quanto menos tokens o seu LLM lê, menos você paga - e o WOS mantém esse número estável conforme a memória cresce.
Todos os idiomas, a mesma precisão.
A recuperação é puramente semântica - apenas embeddings, zero correspondência por palavras-chave ou BM25. Então a qualidade do recall é idêntica, quer seus usuários escrevam em 日本語, 中文, Español ou inglês.
A correspondência lexical, como o BM25, é ajustada ao formato de um idioma específico - morfologia, espaçamento, escrita. Em um store multilíngue, isso significa que a qualidade da recuperação varia por idioma. O WOS não usa nenhuma correspondência lexical, então todos os idiomas passam pelo mesmo caminho.
Um store, três idiomas ao mesmo tempo
Você não escolhe um idioma por store - misture-os livremente. Abaixo, a memória de um único usuário contém japonês, inglês e espanhol ao mesmo tempo, e cada pergunta encontra a memória certa independentemente do idioma. Esta é uma interação real com a API em produção:
# one user, three languages stored together mem.add("彼女はコーヒーより紅茶が好き", user_id="alice") # Japanese mem.add("she works at a design studio in Brooklyn", user_id="alice") # English mem.add("A ella le encanta hacer senderismo los sábados", user_id="alice") # Spanish
"¿Qué bebe ella?" -> 彼女はコーヒーより紅茶が好き "what does she do on weekends?" -> A ella le encanta hacer senderismo los sábados "彼女の仕事は?" -> she works at a design studio in Brooklyn
Sem etapa de tradução, sem detecção de idioma, sem configuração por idioma. Memórias e perguntas são posicionadas pelo significado, não pelo idioma - se o significado corresponde, o idioma não importa.
Por que banimos palavras-chave de propósito
A pontuação lexical, como o BM25, fortalece a recuperação para alguns idiomas mais do que para outros, o que atrapalha quando um store contém muitos idiomas. Por isso, nós a removemos completamente do motor e aplicamos essa regra na revisão de código: com qualquer pontuação lexical no caminho, a qualidade do recall variaria por idioma.
Nenhum modelo é executado sobre suas memórias.
O armazenamento é literal e o motor busca por embeddings - barato, rápido e determinístico. Um modelo nunca é executado sobre as suas memórias armazenadas. O Tablet não usa modelo algum; o Scroll e o Book adicionam um ao redor do motor para resultados mais fortes, mas ele só vê a sua consulta, nunca o que você armazenou.
- Motor determinístico. O motor retorna as mesmas memórias para a mesma consulta, todas as vezes - e é por isso que a variância do nosso benchmark vem apenas do modelo leitor.
- Barato em escala. Sem custo de geração para armazenar ou recuperar, então sua conta acompanha o armazenamento - não o uso de modelo - conforme a memória cresce.
Suas palavras, intocadas
Um design comum executa um modelo de linguagem no momento da escrita para extrair e reescrever "fatos" do texto. Esse design troca três coisas: custo de geração em cada escrita, latência adicional e o armazenamento da paráfrase de um modelo em vez das palavras originais. O WOS faz a troca oposta - armazena o que foi dito, sem alterações, e deixa que o seu LLM faça a interpretação no momento da leitura, com o texto original em mãos.
67,5 %, medido e reproduzível.
67,5 % no BEAM 1M, média de 5 execuções independentes (σ 0,22 %, nenhuma escolhida a dedo), avaliado por gpt-4.1-mini com o prompt de julgamento do próprio benchmark.
No mesmo benchmark, as pontuações variam muito conforme o protocolo de avaliação: o juiz, o prompt e o que a camada de recuperação pode fazer. Avaliamos com o juiz que o próprio repositório dos autores traz, usamos o prompt de julgamento deles como está, não mudamos nada para caber no teste e publicamos o harness, o código de pontuação e o prompt do leitor, para que qualquer um reproduza exatamente os 67,5 %.
O protocolo, em uma única tabela
| Item | O que fazemos |
|---|---|
| Conjunto de dados | BEAM 1M - 35 conversas, 74.630 turnos, 2,2 milhões de memórias, 700 perguntas |
| Juiz | gpt-4.1-mini com temperature 0, rodando o prompt de julgamento do próprio BEAM - o padrão no repositório dos autores, não um juiz escolhido por nós |
| Execuções | 5 execuções independentes, todas as pontuações publicadas, média reportada (σ 0,22 %) |
| Leitor | Modelo leitor e prompt fixos, publicados na íntegra |
O que mantém a honestidade: um juiz terceiro, o prompt do leitor publicado sem alterações, recuperação puramente semântica e todas as execuções reportadas - não apenas a melhor. O motor de recuperação é determinístico - execute de novo e você obtém as mesmas memórias.
Escalamos benchmarks mais difíceis
Testamos no benchmark padrão mais difícil que ainda não conquistamos - e o número é a marca máxima entre todos os modelos WOS, reescrita sempre que um modelo melhor é lançado. Ao superar 94%, avançamos para um benchmark mais difícil.
Benchmark anterior LongMemEval-S Superado
Duas tarifas de token por modelo,
mais $0.0001 por requisição.
Por milhão de tokens mais uma taxa fixa de $0.0001 por requisição, pagamento conforme o uso. Sem assinatura, sem aluguel de armazenamento, sem limites de memória. Você paga quando seu agente escreve ou lê - nunca pelo que ele lembra.
| Modelo | Entrada / 1M | Saída / 1M | |
|---|---|---|---|
| Tablet | $2 | $3 | Disponível |
| Scroll | $4 | $8 | Disponível |
| Book | - | - | A definir |
- $0.0001 por requisição. Uma taxa fixa em cada chamada de API, além do uso de tokens.
- O armazenamento é gratuito. A ingestão paga uma única vez; manter os dados não custa nada. Sem limite de quantidade, sem limite de retenção.
- Nós armazenamos. Nunca treinamos com os dados, os usamos ou olhamos para eles. A memória do seu agente é sua - nós apenas a organizamos para que você possa recuperá-la.
- Por que o Tablet é tão barato: seu motor não executa modelo algum, então nosso custo é embeddings e disco - não GPUs. O Scroll e o Book adicionam um modelo, e é isso que o preço mais alto deles cobre.
Três chamadas: armazenar, recuperar, responder.
Uma única API. A chamada recall() retorna contexto de curto prazo, de longo prazo e do entorno em uma única ida e volta, pronto para inserir no seu prompt.
Armazenar
add() guarda fatos e trocas: as palavras do seu usuário, as do próprio assistente (speaker "me") ou as de uma pessoa nomeada. Incorporado na entrada, sem chamada de LLM.
Recuperar
recall() retorna curto prazo + longo prazo + contexto em uma única chamada - um contexto limitado e de tamanho fixo.
Responder
Entregue esse contexto limitado ao seu LLM - qualquer provedor, sua chave.
from wontopos import Client mem = Client(api_key="wos-...") mem.add("she prefers tea over coffee", user_id="alice") mem.add("I suggested the jasmine tea", user_id="alice", speaker="me") # its own words # one call: short + long + context ctx = mem.recall("what does alice drink?", user_id="alice")
Memórias carregam um falante. O padrão são as palavras do seu usuário, speaker "me" guarda o que o próprio assistente disse, e um nome como "Bob" lembra quem disse, permitindo lembrar por pessoa.
Seu primeiro recall em 5 minutos.
Uma chave, uma linha de instalação, três chamadas - seu agente tem memória. Cada trecho de código nesta página foi realmente executado; as respostas são mostradas na íntegra.
Obtenha uma chave de API
Crie uma no console. Uma chave de 155 caracteres que começa com wos-live- é exibida uma única vez. Guarde-a em uma variável de ambiente - nunca no código.
Instale
pip install wontopos # Python npm install wontopos # TypeScript / JavaScript cargo add wontopos # Rust # curl - nothing to install, just set WOS_API_KEY # latest: SDK v2.2.32 · MCP v1.0.15
Crie um store, depois armazene & recupere
Um store é o user_id sob o qual você lê e escreve. Stores são explícitos: crie um primeiro (a chamada abaixo), depois armazene e recupere sob ele. Armazenar - embeddings gerados na entrada, sem chamada de LLM. Recuperar - curto prazo + longo prazo + contexto em uma única ida e volta.
from wontopos import Client
mem = Client(api_key="wos-live-...", user_id="alice") # set the store once
mem.create_store() # create it (stores are explicit)
mem.add("she prefers tea over coffee") # no user_id needed
# one call → short-term + long-term + context
ctx = mem.recall("what does alice drink?"){"user_id": "alice", "status": "created"}{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}user_id ao cliente e todas as chamadas o usam - sem precisar repeti-lo; sobrescreva uma chamada específica passando user_id a ela. Stores são explícitos: armazenar em um store que não existe, ou recuperar dele, retorna 404 - crie-o primeiro. Toda conta começa com um store default, então sem nenhum user_id o caminho de configuração zero simplesmente funciona. Veja Stores para listá-los e gerenciá-los.recall() retorna quatro blocos - short_term (turnos recentes), long_term (memórias relevantes), context (o que cercava a melhor correspondência) e uma instruction dizendo ao LLM como usá-los. Insira tudo isso no seu prompt.
Todos os métodos, por linguagem →
Um cliente, configurações diferentes
mem = Client.from_env() # reads WONTOPOS_API_KEY
scroll = mem.with_model("scroll-1.2") # this copy only: another engine
alice = mem.with_user("alice") # this copy only: another default storeStores - criar, listar, excluir.
Um store é o user_id sob o qual você lê e escreve - um espaço de memória isolado por usuário final, agente ou tópico. Stores são explícitos: crie um antes de armazenar nele ou recuperar dele, ou a chamada retorna 404. Toda conta começa com um store default, então você pode começar sem uma chamada de criação.
mem.create_store("alice") # create (idempotent)
mem.list_stores() # [{"user_id","created_at"}, ...]
mem.delete_store("alice") # delete the store + all its memories{ "user_id": "alice", "status": "created" } // "exists" if it already did{ "collections": [
{ "user_id": "default", "created_at": "2026-06-26T02:23:14Z" },
{ "user_id": "alice", "created_at": "2026-06-26T02:24:01Z" }
], "count": 2 }{ "error": { "type": "not_found_error",
"message": "Store 'ghost' does not exist. Create it first with
POST /api/v1/memory/collection {\"user_id\":\"ghost\"}, then store or recall." } }"alice", "user_42") para manter a memória de cada pessoa separada, ou um único store default para um agente pessoal. Você também pode criar e navegar pelos stores no console (Memory ids → Issue) sem escrever código. Excluir um store é permanente - remove todas as memórias sob ele. Os ids de armazém são dobrados antes de serem guardados: passam a minúsculas e tudo fora de [a-z0-9_] torna-se _, pelo que Alice.Smith e alice-smith designam o mesmo armazém. Um segundo id que dobre sobre um existente é recusado com 409 em vez de partilhado em silêncio. O id tem também de corresponder a [A-Za-z0-9][A-Za-z0-9._-]{0,63}, pelo que um endereço de e-mail ou um nome não latino não pode ser um id de armazém - use um identificador interno.Listar e apagar stores
mem.list_stores() # [{"user_id","created_at"}, …]
mem.delete_store("alice") # the store and every memory in itRecalls repetidos, por um décimo do preço.
Ative por solicitação e o WOS armazena em cache o resultado da busca sob o texto da consulta, com as mesmas regras de prefixo do prompt caching dos LLMs. Enquanto o cache está quente, uma consulta repetida ou estendida reutiliza o resultado anterior, e a parte em cache é cobrada a 10% da tarifa normal por token.
Uma conversa, três turnos
É isto que realmente acontece quando um agente continua conversando com sua memória. Cada turno envia a conversa acumulada como consulta, com cache_control ligado.
A consulta inteira é buscada e armazenada em cache: entrada a 2x (TTL de 5 minutos).
Só a frase do Bob é transformada em embeddings e buscada. A parte antiga custa 0,1x, a frase nova 2x, e o cache agora termina nela.
Nenhuma chamada ao motor. Tudo a 0,1x: o desconto de 90%.
As tarifas
| Operação | Cobrança de tokens | O que significa |
|---|---|---|
| Escrita de cache - TTL 5 minutos | 2× | A primeira solicitação. Seu resultado é mantido por 5 minutos, e cada leitura desliza a janela para frente. |
| Escrita de cache - TTL 1 hora | 3× | A primeira solicitação, mantida por uma hora inteira. |
| Leitura de cache - acerto ou acerto de prefixo | 0.1× | Toda solicitação após a escrita: a parte em cache custa um décimo da tarifa normal por token. |
Quanto isso economiza
Um exemplo concreto: seu agente envia uma conversa de 3.000 tokens como consulta e a repete ou continua 10 vezes em cinco minutos. Sem cache, são 30.000 tokens de entrada a preço cheio. Com um cache de 5 minutos, são 6.000 pela primeira escrita (2x) mais cerca de 2.700 pelas nove leituras em cache: 8.700 tokens cobrados, 71% menos. Quanto mais longa a conversa, maior a economia.
A regra do prefixo
A correspondência é feita no início da consulta. Se o início permanece idêntico e apenas texto novo é acrescentado ao final, a parte em cache é reutilizada e apenas a parte nova é buscada. Se algo muda antes do fim do texto em cache, nada pode ser reutilizado.
cached [ A B C D E F G ] ○ [ A B C D E F G ] E ✗ [ B C D E F G ] E
○ acerto - o início não mudou, E é a única parte nova
✗ erro - o início mudou, então a consulta inteira é buscada e armazenada em cache novamente
Três regras para lembrar
- Estender rearmazena até a nova cauda. Depois de [A B C D E F G] + E, o cache agora termina em E: a cauda é cobrada uma vez na tarifa de escrita, e o próximo turno pode casar todo o A..E como prefixo de novo.
- Um único prefixo contíguo por solicitação. Uma consulta não pode ser dividida em dois segmentos de cache; apenas o seu início pode casar.
- Escritas invalidam na hora. Qualquer store, store-turn, bulk-store, forget, supersede ou exclusão do store descarta o cache dele, então uma resposta em cache nunca pode ficar obsoleta.
Como ativar
hits = mem.search(
"...the conversation so far...", user_id="alice",
cache_control={"ttl": "5m"}, # or "1h"
){ "memories": [ ... ],
"cache": { "status": "hit", // "write" | "hit" | "extend"
"ttl": "5m",
"cache_read_input_tokens": 412,
"cache_creation_input_tokens": 0 } }Você não precisa de um SDK para nada disso. O caching é um campo em uma chamada HTTP, então funciona a partir de qualquer linguagem de programação. A aba curl é a receita universal, e os SDKs de Python, TypeScript e Rust são apenas invólucros de conveniência sobre exatamente a mesma chamada.
Memória que sabe quem disse.
As pessoas lembram por pessoa: o que o Bob prometeu, o que você disse que faria. Marque cada memória com um falante e seu agente faz o mesmo, em todos os modelos Tablet e Scroll.
Um time, três memórias
Um armazenamento mantém muitas vozes separadas. Registre uma pessoa uma vez, salve cada fala com seu falante e depois pergunte por pessoa.
O armazenamento agora conhece o Bob. O limite de 50 é contado aqui, no registro; chamadas de salvamento nunca retornam erro de limite.
A memória agora é do Bob: toda busca que a devolve indica isso.
A fala do próprio assistente também vira memória, e "me" nunca conta para o limite.
Só as palavras do Bob voltam. As palavras de uma pessoa nunca voltam como as de outra.
Três regras para lembrar
- "me" é o próprio assistente. Nunca registrado, nunca contado. Reservado e minúsculo:
speaker: "Me"ou"ME"retorna400 invalid_request_errorem vez de ser convertido em silêncio. - O limite é contado no registro: 50 por armazenamento para começar. Registrar além disso retorna
400 invalid_request_errorcomspeaker_limit: 50no corpo do erro. Salvar com um nome não registrado também retorna400e nada é salvo. Filtrar a busca por um nome não registrado retorna404 not_found_error. Ramifique pelo status e pelos campos, não pelo texto da mensagem; planejamos aumentar o limite. - Rótulos vivem em toda leitura. Resultados de busca, o contexto de longo prazo do recall e resultados de engram carregam seu falante — o modelo sempre sabe de quem são as palavras. Passe speaker numa busca para obter só as de uma pessoa. Um supersede mantém o falante; forget o remove.
- Nomes são Unicode: qualquer idioma funciona. さくら, Иван e 하늘 são falantes válidos, e a atribuição se comporta igual em todos os idiomas. A correspondência é exata após trim e normalização Unicode, então
Bobebobsão duas pessoas. Nomes vão até 80 caracteres.
# POST /speakers past the limit { "type": "error", "error": { "type": "invalid_request_error", "message": "This store already has 50 registered speakers, ...", "speaker_limit": 50 } } # store with an unregistered name → 400, nothing stored { "type": "error", "error": { "type": "invalid_request_error", "message": "speaker 'Bob' is not registered in this store. Register it first: ...", "speaker": "Bob" } } # search filtered by an unregistered name → 404 { "type": "error", "error": { "type": "not_found_error", "message": "speaker 'Bob' is not registered in this store.", "speaker": "Bob" } }
Duas notas de escopo. speaker acompanha add / store: add_turn lembra a troca inteira, e rótulos por pessoa e o filtro vêm de memórias com speaker explícito. E passagens de sessão (expand) são compostos de várias memórias, então não carregam rótulo; um filtro speaker sempre devolve memórias atômicas e rotuladas. E uma escrita cujo significado esteja próximo o suficiente de uma memória já guardada é descartada: a correspondência é semântica, não textual. Esse store devolve status "duplicate" com uma nota explícita, não guarda nada e não anexa falante. Um facto genuinamente novo que varie apenas num detalhe de um existente ("alergia a marisco" depois de "alergia a amendoim") cai na mesma regra, por isso leia status em vez de assumir que a escrita foi concluída.
Testamos do jeito difícil: memórias sem nomes no texto, lembradas por pessoa. A atribuição vem do registro de falantes, não de casamento de palavras, então funciona igual em qualquer idioma.
Como usar
mem.add_speaker("Bob", user_id="alice") # once per person; "me" needs no registration
mem.add("Bob said the deadline moved to Tuesday", user_id="alice", speaker="Bob")
mem.add("I promised the summary by Friday", user_id="alice", speaker="me")
hits = mem.search("what did Bob say about the deadline?", user_id="alice", speaker="Bob")
mem.list_speakers(user_id="alice")
mem.remove_speaker("Bob", user_id="alice") # memories stay, the tag goes{ "memories": [
{ "content": "Bob said the deadline moved to Tuesday",
"speaker": "Bob", ... } ] }{ "user_id": "alice",
"speakers": [ { "speaker": "Bob", "memories": 2, "created_at": "2026-07-10T04:20:39Z" } ],
"count": 1, "limit": 50 }A lista mostra quem o armazenamento conhece, com contagens por pessoa contra o limite. Remover apaga só o registro: as memórias ficam, apenas o rótulo do nome vai embora.
Ler as memórias de uma pessoa
by_speaker retorna o que uma pessoa disse, da mais recente para a mais antiga, sem consulta. "me" devolve as próprias palavras do assistente. A paginação por cursor é a mesma das imagens: devolva next_before e next_skip_ids.
page = mem.by_speaker("Bob", limit=50)
page["memories"], page["chunks"]| Campo | O que faz |
|---|---|
| memories | As memórias, da mais recente para a mais antiga. O mesmo formato que uma busca retorna. |
| chunks | Os fragmentos em nível de frase por trás dessas memórias - o que uma exclusão de fato removeria. Normalmente maior que o número de memórias; exiba-o antes de alguém confirmar uma exclusão. Também informado como points_to_delete. |
| next_before | Cursor para a próxima página, junto com next_skip_ids. Os dois são necessários porque memórias podem compartilhar o mesmo timestamp. |
speaker aqui é a etiqueta gravada no momento do armazenamento, não uma busca sobre o texto. Uma memória armazenada sem falante é alcançável por busca, mas nunca por by_speaker, inclusive sob "me".Listar, navegar e remover falantes
mem.list_speakers() # who is registered
mem.by_speaker("Bob") # what Bob said, newest first
mem.remove_speaker("Bob") # unregister; the memories stayImagens
Uma memória pode carregar uma imagem. O motor indexa a imagem, então uma consulta em texto em qualquer idioma a encontra mesmo quando o registro não tem legenda, título nem texto alternativo.
Armazenar uma
Passe um objeto image para a chamada comum de add. content pode ficar vazio; nesse caso a imagem é pesquisável sozinha.
mem.add("at the beach", image={"data": b64}) # caption + image
mem.add("", image={"data": b64}) # the image IS the memorydata é obrigatório. O prefixo data:image/jpeg;base64, e as quebras de linha adicionadas por base64 e openssl são removidos para você.
| Campo | O que faz |
|---|---|
| data | Base64 da imagem. Obrigatório. O teto de tamanho é uma configuração do servidor, não uma constante do SDK - /health o informa como memory.images.max_bytes. |
| reference | Onde fica a sua própria cópia do original. Armazenado como string e nunca acessado por nós. |
| taken_at | RFC3339, normalmente vindo do EXIF. Preenche event_date quando esse campo está vazio, então a memória é ordenada por quando a imagem foi tirada, não por quando foi enviada. |
Encontrar uma
Não existe uma busca separada para imagens. search e recall retornam imagens junto com texto, classificadas em conjunto.
Trabalhar com as que já existem
data, mime = mem.get_image(memory_id=mid)
page = mem.list_images(limit=50) # page["count"] = store total
mem.forget_image(memory_id=mid, preview=True)| Chamada | O que faz |
|---|---|
| get_image | Os bytes originais, como (bytes, content_type). O tipo é detectado a partir dos bytes, não do nome com que o upload foi enviado. Uma memória sem imagem lança um erro em vez de retornar algo vazio. |
| list_images | Uma página, da mais recente para a mais antiga, mais count - o total do store, não o tamanho da página. A paginação é por cursor: devolva next_before e next_skip_ids. Os dois são necessários porque imagens podem compartilhar o mesmo timestamp. |
| forget_image | Remove a imagem e mantém o texto. Uma imagem armazenada sem legenda é a memória, então nesse caso a memória também é apagada. |
Passe preview=True para forget_image para obter memory_kept sem alterar nada. iter_images pagina para você.
Quanto custa uma imagem
Uma imagem é cobrada em tokens, a mesma unidade do texto. Tokens = área em pixels / 556,7. Acima de 1.568 px no lado maior a contagem é feita a 1.568 px, então uma imagem de 2.500 px custa o mesmo que uma de 1.568 px.
| Imagem | Contabilizada em | Tokens |
|---|---|---|
| 700 × 700 | as sent | 881 |
| 1000 × 1000 | as sent | 1,797 |
| 1568 × 1568 | as sent | 4,417 |
| 1920 × 1080 | 1568 × 882 | 2,485 |
| 2500 × 1875 | 1568 × 1176 | 3,313 |
| 2500 × 2500 | 1568 × 1568 | 4,417 |
Teto: 4.417 tokens por imagem. Reservamos esse teto do seu saldo antes da chamada e cobramos depois o valor medido, que nunca é maior.
Quantas retornam
Padrão 1, máximo de 5 imagens por resposta. Cinco imagens ficam perto de 20.000 tokens.
| Campo | O que faz |
|---|---|
| max_images | De 0 a 5. Imagens que uma única resposta pode carregar. Padrão 1. 0 retorna apenas texto. Fora do intervalo é recusado, não ajustado ao limite. |
verify
verify permite que uma busca execute passadas adicionais. Cada passada exclui o que as passadas anteriores retornaram, então uma segunda passada alcança memórias que a primeira não alcançou.
Um inteiro de 0 a 3 em search e recall. É o número de passadas adicionais, então 3 permite quatro recuperações. Padrão 0.
hits = mem.search("what did I eat", verify=3)
# the SDKs hand back the memories; `verify_used` is on the HTTP response (curl tab)Nenhum modelo de linguagem é executado nesse laço
As passadas carregam os ids já retornados; o motor os exclui e busca além deles. A consulta não é reformulada, então os resultados são determinísticos para uma dada requisição e nenhuma credencial de modelo está envolvida. O seu código decide se gasta outra passada.
O que você envia e o que volta
| Chamada | O que faz |
|---|---|
| verify | De 0 a 3. Passadas adicionais permitidas. Fora do intervalo é rejeitado com um 400, não ajustado ao limite em silêncio. |
| verify_used | Quantas passadas adicionais foram de fato executadas. Pode ser menor do que o solicitado. |
As passadas param assim que uma delas não retorna nada novo, e as passadas não usadas não são cobradas. Se uma passada posterior falhar, os resultados reunidos até então são retornados.
MCP - memória para ferramentas de IA
O núcleo do WOS é a API e os SDKs. O servidor MCP é um complemento sobre eles: a mesma memória, plugada em ferramentas que você não construiu - Claude Code, Claude Desktop, Cursor.
Uma linha de instalação dá ao agente nove ferramentas de memória que ele usa sozinho. E como a memória vive na sua conta, o que uma ferramenta escreve, todas as outras lembram - inclusive agentes que você constrói com o SDK.
O que dá para fazer com isso
- Um Claude Code que lembra do seu projeto. Decisões, correções, preferências - lembrados na próxima sessão sem reexplicar nada.
- Comece no ChatGPT, continue no Claude. Mesmo store, mesma memória - a conversa atravessa ferramentas em vez de recomeçar.
- Seu próprio agente continua no circuito. O que o Claude Code aprende, um agente do SDK lembra - e o que seu agente guarda, o Claude Code lembra de volta.
Funciona no Claude Code, Claude Desktop, Cursor, Windsurf e qualquer host MCP. O ChatGPT alcança a mesma memória via Actions mais a spec OpenAPI.
Instalação
claude mcp add wontopos --env WONTOPOS_API_KEY=wos-live-... -- npx -y wontopos-mcpO agente recebe nove ferramentas - recall · remember · search · update · forget · list_memories · engram · stats · create_store - cada uma descrita para que ele saiba sozinho quando usá-las.
Spec OpenAPI
O mapa completo e legível por máquina da API - cada endpoint, requisição, resposta e erro.
OpenAPI é o formato padrão da indústria para descrever uma API HTTP em um arquivo legível por máquinas.
https://api.wontopos.com/openapi.jsonO que dá para fazer com isso
Postman: File → Import → cole a URL e todos os endpoints viram uma coleção clicável. ChatGPT: crie um GPT, adicione uma Action, cole a mesma URL. Codegen: openapi-generator -i .../openapi.json -g go gera um cliente em uma linguagem que não publicamos.
Importe no Postman, gere um cliente em uma linguagem que não publicamos, conecte ChatGPT Actions ou rode checks de contrato no CI. Um teste a fixa às rotas reais: não há como desviar.
llms.txt
Toda a API em uma página de texto que uma IA consegue ler.
llms.txt é uma convenção da web: uma página de texto puro na raiz do site que conta a uma IA tudo o que ela precisa sobre um produto.
https://wontopos.com/llms.txtColoque na sua IDE ou agente de código e ele sabe como construir sobre o WOS - auth, endpoints, padrões, erros. Atualizado a cada release.
Os mesmos fatos da spec OpenAPI, público diferente: a spec é estrutura precisa para ferramentas; este arquivo é prosa que uma IA (ou uma pessoa) lê de uma vez. Ambos atualizam a cada release.
Sua memória dentro de cada ferramenta de IA
Um único comando dá ao Claude Code, Claude Desktop, Cursor ou qualquer host MCP uma memória de longo prazo baseada na sua conta WOS. Sem código de integração: o agente recebe nove ferramentas de memória e decide quando usá-las.
Instalação
Claude Code, uma linha (crie antes uma chave no console):
claude mcp add wontopos --env WONTOPOS_API_KEY=wos-live-... -- npx -y wontopos-mcp
# pick which store it remembers into (optional): add --env WONTOPOS_USER_ID=my-projectAdd to Cursor → · Add to VS Code →
Env opcional: WONTOPOS_USER_ID define o store padrão, WONTOPOS_MODEL o motor, WONTOPOS_BASE_URL uma implantação self-hosted. WONTOPOS_READ_ONLY=1 muda para somente leitura (só recall/busca/listagem).
Antes de compartilhar um store
- Use uma chave dedicada. Chaves carregam seu workspace: uma chave só para MCP delimita o que as ferramentas conectadas podem tocar - e você pode rotacioná-la no console sem mexer nas chaves do app.
- Modo somente leitura.
WONTOPOS_READ_ONLY=1não registra nenhuma ferramenta de escrita: o agente pode lembrar, buscar, listar memórias, executar engramas e ler estatísticas, mas não guardar, atualizar, esquecer ou apagar. Ideal para agentes que devem consultar a memória, não possuí-la. - Mantenha a confirmação de ferramentas ligada. Hosts MCP perguntam antes de rodar ferramentas por padrão - deixe ligada principalmente para
forget, pois exclusões valem para todas as ferramentas do store. - Tudo o que for guardado pode ser lembrado por qualquer ferramenta com a chave. Nunca guarde segredos - chaves de API, senhas - como memórias.
- Memórias lembradas são dados, não instruções. As descrições das ferramentas dizem isso explicitamente ao agente. Ainda assim, não guarde texto de terceiros não confiável em um store que um agente autônomo obedece.
- Exclusões também são compartilhadas. Um forget ou delete_all de uma ferramenta apaga para todas.
- "me" é o agente que escreve no store. Se vários agentes compartilham um store, as vozes "me" se misturam. Dê a cada agente seu próprio store (
WONTOPOS_USER_ID) para identidades separadas. - Uma conta paga. Todas as ferramentas conectadas consomem o mesmo saldo e limite de taxa.
Depois, é só conversar
O agente chama a ferramenta remember. Fica guardado de forma durável: o fim da sessão não muda nada.
Uma sessão nova não tem histórico. O agente chama recall e responde de memória: às sextas.
Coisas para dizer
- "Este repo usa pnpm, lembre disso" →
rememberguarda; a próxima sessão já sabe. - "Qual formato de erro combinamos semana passada?" →
recalltraz a decisão de volta ao contexto. - "Na verdade, o prazo passou para sexta" → o agente vê que contradiz o que lembrou e chama
updatepara corrigir essa memória no lugar. - "Isso está errado, esqueça" → o agente acha o id e chama
forget- seu host pede confirmação antes. - "O que você lembra sobre mim?" →
list_memoriespercorre tudo o que está guardado, para o agente responder ou organizar.
Nada de especial para formular: são frases comuns, não comandos. O agente lê a descrição de cada ferramenta e escolhe sozinho.
As nove ferramentas
recall- Contexto em uma chamada: turnos recentes mais memórias relevantes. A descrição instrui o agente a chamá-la primeiro sempre que o contexto passado importar.remember- Guarda um fato ou decisão durável.speaker: "me"marca as palavras do próprio agente; um nome registrado, quem disse.search- Busca semântica, com um filtrospeakerpor pessoa — efilterspara limitá-la por DATA ou tema ("o que decidimos em junho?"), o único eixo que o significado sozinho não consegue estreitar.update- Substitui uma memória cujo fato mudou, mantendo o rastro em vez de apagá-lo.forget- Apaga uma memória pelo id.list_memories- Percorre tudo o que está armazenado, para responder "o que você lembra de mim?" ou fazer limpeza.engram- Executa um pipeline multi-salto embutido (deep_recall, timeline, gather) quando uma única busca não basta.stats- Quanto há em um repositório - útil antes de uma limpeza e para confirmar que uma escrita chegou.create_store- Stores são explícitos: um por usuário final, projeto ou agente.
SDK ou MCP?
- O SDK vai dentro de um app que você escreve. Seu código decide exatamente quando guardar e o que lembrar - determinístico, tipado, versionado. Construindo um produto? SDK.
- O MCP se pluga em uma ferramenta de IA que você não escreveu. O agente decide quando usar a memória, guiado pelas descrições - zero código. Para Claude Code, Claude Desktop, Cursor ou dar memória a um assistente pronto.
Por baixo, a mesma API e os mesmos stores - um app feito no SDK e uma sessão do Claude Code via MCP compartilham uma memória. Escolha por superfície, não um ou outro.
Uma memória através de todas as ferramentas
A memória pertence à conta, não à ferramenta. O mesmo store escrito do ChatGPT (Actions mais a spec OpenAPI) é lembrado no Claude Code e nos seus próprios agentes, e vice-versa: uma conversa iniciada em uma ferramenta continua em outra.
E como é um único store, você pode sair do Claude Code e continuar a conversa onde constrói: um agente do SDK com a mesma chave e store lembra tudo o que o Claude Code acabou de aprender - e o que seu agente guarda, o Claude Code lembra na próxima sessão.
npx wontopos-mcp): com este método a sua chave fica no seu ambiente e nunca nos é enviada como parte de uma sessão MCP. Envolve o SDK de TypeScript, pelo que as repetições automáticas, a recusa de redirecionamentos e o mascaramento da chave se aplicam tal e qual.Claude Code
O caminho principal: um comando no terminal e toda sessão começa com memória.
- Crie uma chave de API no console. A chave carrega seu workspace: uma chave = um espaço de memória.
- Registre o servidor.
--scope usero disponibiliza em todos os projetos; sem ele, só o projeto atual o vê. - Confira: rode
/mcpdentro do Claude Code -wontoposdeve aparecer com nove ferramentas. - Torne automático: uma linha no seu
CLAUDE.md- "quando o contexto passado importar, chame wontopos recall primeiro" - e toda sessão começa com memória sem pedir.
claude mcp add wontopos --scope user \
--env WONTOPOS_API_KEY=wos-live-... -- npx -y wontopos-mcp
# pick a store (optional): add --env WONTOPOS_USER_ID=my-projectClaude Desktop
Adicione o bloco abaixo ao claude_desktop_config.json (Configurações → Developer → Edit Config), reinicie o app e as nove ferramentas aparecem. Nota: o claude.ai na web e no celular precisa de um servidor MCP remoto, que o WOS ainda não oferece - o app de desktop é o caminho suportado.
# claude_desktop_config.json
{ "mcpServers": {
"wontopos": {
"command": "npx",
"args": ["-y", "wontopos-mcp"],
"env": { "WONTOPOS_API_KEY": "wos-live-...",
"WONTOPOS_USER_ID": "my-project" }
} } }Cursor
Adicione o bloco abaixo ao ~/.cursor/mcp.json - ou aperte o botão de um clique - e reinicie o Cursor. O agente pega as nove ferramentas.
# ~/.cursor/mcp.json
{ "mcpServers": {
"wontopos": {
"command": "npx",
"args": ["-y", "wontopos-mcp"],
"env": { "WONTOPOS_API_KEY": "wos-live-...",
"WONTOPOS_USER_ID": "my-project" }
} } }VS Code
O VS Code (modo agente do Copilot) lê servidores MCP de .vscode/mcp.json do projeto - adicione o bloco abaixo ou aperte o botão de um clique.
# .vscode/mcp.json
{ "servers": {
"wontopos": {
"command": "npx",
"args": ["-y", "wontopos-mcp"],
"env": { "WONTOPOS_API_KEY": "wos-live-...",
"WONTOPOS_USER_ID": "my-project" }
} } }Windsurf
O Windsurf (Cascade) lê ~/.codeium/windsurf/mcp_config.json: adicione o bloco abaixo e recarregue - as mesmas nove ferramentas aparecem.
# ~/.codeium/windsurf/mcp_config.json
{ "mcpServers": {
"wontopos": {
"command": "npx",
"args": ["-y", "wontopos-mcp"],
"env": { "WONTOPOS_API_KEY": "wos-live-...",
"WONTOPOS_USER_ID": "my-project" }
} } }ChatGPT
Os conectores MCP do ChatGPT só aceitam servidores remotos, então o caminho suportado hoje é um GPT personalizado com uma Action: crie um GPT, adicione uma Action, cole a URL da spec OpenAPI abaixo e defina sua chave como header de auth. Esse GPT chamará a mesma memória das suas outras ferramentas.
# GPT → Configure → Actions → Import from URL
https://api.wontopos.com/openapi.json
# Authentication: API Key · Header name: X-API-KeyMesmo store, mesma memória: o que o ChatGPT guarda pela Action, o Claude Code lembra pelo MCP - e vice-versa.
Gemini CLI
O Gemini CLI lê servidores MCP de ~/.gemini/settings.json: adicione o bloco abaixo, reinicie a CLI e as mesmas nove ferramentas aparecem lá também.
# ~/.gemini/settings.json
{ "mcpServers": {
"wontopos": {
"command": "npx",
"args": ["-y", "wontopos-mcp"],
"env": { "WONTOPOS_API_KEY": "wos-live-...",
"WONTOPOS_USER_ID": "my-project" }
} } }Python - todos os métodos, três grupos.
Escrever, ler, excluir. Todos os exemplos abaixo foram executados contra a API em produção em 2026-08-01; as respostas estão na íntegra.
pip install wontopos
from wontopos import Client mem = Client(api_key="wos-live-...") # or read from an env var
Escolha um modelo
A chave de API escolhe qual memória (sua conta); o modelo escolhe qual motor a lê. Todos os modelos compartilham uma mesma memória, então você pode armazenar com um e recuperar com outro. Defina um padrão no cliente; sobrescreva uma chamada específica passando model=.
mem = Client(api_key="wos-live-...", model="tablet-1") # default engine mem.recall("...", user_id="alice") # tablet-1 mem.recall("...", user_id="alice", model="scroll-1") # or pick a model per call
list_models
O catálogo - os ids que você pode passar em model e se cada um está disponível. Modelos com memory: "shared" leem o mesmo store; "isolated" mantém o seu próprio. Não requer chave de API.
mem.list_models()[{"id": "tablet-1", "name": "Tablet 1", "available": true, "memory": "shared"},
{"id": "tablet-2", "name": "Tablet 2", "available": true, "memory": "shared"},
{"id": "scroll-1", "name": "Scroll 1", "available": true, "memory": "shared"},
{"id": "scroll-1.2", "name": "Scroll 1.2", "available": true, "memory": "shared"}]ping
Confirme a conexão e que sua chave de API funciona: uma verificação de uma linha.
mem.ping() # True, or raises AuthenticationError / PaymentRequiredError
O catálogo acima sempre reflete os modelos disponíveis neste momento - passe qualquer outro id e você recebe um erro claro. Novos modelos aparecem nele automaticamente quando são lançados.
Escrever
add
Armazena uma memória. Embeddings gerados na entrada - sem chamada de LLM, você paga apenas embeddings.
mem.add("she prefers tea over coffee", user_id="alice") mem.add("I promised the summary by Friday", user_id="alice", speaker="me") # its own words - no registration needed
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}add_turn
Armazena um turno de conversa (usuário + assistente) na memória de curto e de longo prazo de uma só vez.
mem.add_turn("hi", "hello!", user_id="alice")
{"status": "ok"}speaker
Cada memória pode carregar quem a disse. Registre uma pessoa uma vez e depois passe o nome como speaker; "me" (as palavras do próprio assistente) nunca precisa de registro. A busca também aceita speaker, para lembrar só as palavras de uma pessoa.
mem.add_speaker("Bob", user_id="alice") # once per person; "me" needs no registration mem.add("I promised to send the report on Friday", user_id="alice", speaker="me") mem.add("Bob said the deadline moved to Tuesday", user_id="alice", speaker="Bob") mem.search("what did Bob say about deadlines?", user_id="alice", speaker="Bob")
[{"content": "Bob said the deadline moved to Tuesday", "speaker": "Bob", ...}]add_bulk
Faz backfill de um grande bloco de texto. Fragmentado e convertido em embeddings no servidor - ideal para importar um histórico existente.
mem.add_bulk("Alice moved to Brooklyn in March. She works at a design studio downtown.", user_id="alice")
{"elapsed_secs": 0.154154944, "status": "ok", "stored": 1, "total_chunks": 1}update
Um fato mudou. A memória antiga é marcada como substituída (mantida para histórico); a nova toma o lugar dela no recall.
mem.update("576700aa-...", "she switched to coffee this year", user_id="alice")
{"new_memory_id": "07e94433-b7cc-4e49-8d8f-f37fc1a392b7",
"old_memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "superseded"}Ler
search
Busca semântica, os mais relevantes primeiro. Puramente embeddings - sem correspondência por palavras-chave, então qualquer idioma encontra qualquer memória. O SDK retorna o array de memórias diretamente; o corpo HTTP bruto é mostrado abaixo. Num modelo com faixa própria (Scroll 1.2+) o serviço responde em duas faixas e o SDK devolve ambas fundidas, pelo que o array pode conter MAIS do que max_results. Dimensione a janela do prompt pelo que recebe, não pelo número pedido.
r = mem.search("what does she drink?", user_id="alice", limit=1)
[{
"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624",
"content": "she prefers tea over coffee",
"category": "general",
"time_bucket": "2026-06",
"importance": 0.3,
"similarity": 0.6316057443618774,
"is_superseded": false,
"superseded_by": null,
"created_at": "2026-07-10T04:20:39.688276876Z"
}]| Campo | Significado |
|---|---|
| similarity | Similaridade bruta de embedding com a sua consulta (0–1). |
| is_superseded | Verdadeiro se este fato foi substituído por update(). |
| search_ms | Tempo de recuperação no servidor. |
recall
Uma única ida e volta retorna tudo o que o seu LLM precisa - cole o resultado direto no seu prompt: um contexto limitado e de tamanho fixo, não importa quanto você tenha armazenado.
ctx = mem.recall("what does she drink?", user_id="alice")
{"short_term": {"count": 2, "turns": [{"role": "user", "content": "hi", ...}]},
"long_term": {"count": 4, "memories": [{"content": "she prefers tea over coffee",
"similarity": 0.63, ...}]},
"context": {"count": 4, "around_top_memory": [
"[match] she prefers tea over coffee",
"[after] Alice moved to Brooklyn in March. ..."]},
"instruction": "Use short_term for recent context, long_term for relevant
past memories, context for surrounding conversation of the
most relevant memory."}history
Turnos recentes da conversa (memória de curto prazo), do mais antigo para o mais recente.
turns = mem.history("alice")
{"count": 2, "turns": [
{"role": "user", "content": "hi", "timestamp": "2026-07-10T04:20:40.989011337Z"},
{"role": "assistant", "content": "hello!", "timestamp": "2026-07-10T04:20:40.989013416Z"}
], "user_id": "alice"}stats
Contagens de memórias de um usuário.
mem.stats("alice")
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}get
Busca uma memória pelo id - o id que add ou list_memories retornou. Apenas o texto original armazenado e seus metadados, nunca o vetor. Um id de outro store, ou uma memória apagada ou invalidada, retorna 404.
m = mem.get("alice", memory_id="576700aa-...")
{"id": "576700aa-...", "content": "she prefers tea over coffee",
"category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
"is_superseded": false, "superseded_by": null}{"memory": {"id": "8bd090de-...", "content": "the office moved to the seventh floor in June",
"category": "general", "created_at": "2026-07-31T18:20:30.531518060+00:00", "event_date": null,
"is_superseded": false, "superseded_by": null}, "user_id": "docs_livetest"}list_memories
Lista as memórias de um armazenamento - apenas o texto original que você guardou e seus metadados, nunca o vetor. Paginado por cursor: reenvie o next_cursor retornado para a próxima página.
page = mem.list_memories("alice", limit=100)
{"count": 2, "next_cursor": null, "memories": [
{"id": "576700aa-...", "content": "she prefers tea over coffee",
"category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
"is_superseded": false, "superseded_by": null}
]}iter_memories · export_memories
Percorra todas as memórias sem gerenciar o cursor, ou traga o armazenamento inteiro de uma vez.
for m in mem.iter_memories("alice"): # every page, no cursor bookkeeping print(m["id"], m["content"]) everything = mem.export_memories("alice") # the whole store as a list
Excluir
delete
Exclui uma única memória pelo id.
mem.delete("alice", memory_id="576700aa-...")
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}delete_all
Apaga tudo de um usuário - uma única chamada, pronta para o GDPR.
mem.delete_all("alice")
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}Erros e confiabilidade
Cada falha é um erro tipado - capture o específico (limite de taxa, autenticação, pagamento) ou todos com o WosError base.
from wontopos import PaymentRequiredError, NotFoundError try: mem.add("...", user_id="alice") except NotFoundError: mem.create_store("alice") # store didn't exist yet except PaymentRequiredError: top_up() # out of credit - don't retry
rate_limit
Leia a cota restante após qualquer chamada e desacelere antes de bater no limite.
mem.search("...", user_id="alice") rl = mem.rate_limit # {"limit": 150, "remaining": 3, "reset": ...}
search_self
As duas trilhas em uma única chamada num modelo de memória própria (Scroll 1.2+): o que os outros disseram e as palavras do PRÓPRIO agente - separadas, para que quem lê nunca confunda quem falou.
r = mem.search_self("what did I promise?", user_id="alice") r["memories"] # what others said / general memories r["self_memories"] # the agent's OWN words (speaker "me")
list_engrams
Pergunte ao serviço quais engramas e formas de entrega o modelo selecionado consegue executar, em vez de fixar nomes que ficam desatualizados assim que um novo é lançado.
cat = mem.list_engrams() [e["name"] for e in cat["engrams"]] # ask, never hard-code
filters
Restringe a busca a parte do repositório. Aplicado antes da ordenação, então você recebe as melhores correspondências dentro do filtro - não um top-N filtrado depois.
mem.search("what did we decide", user_id="alice", filters={ "categories": ["work"], "event_from": "2026-01-01", # when it HAPPENED })
idempotency_key
Torna seguro repetir uma escrita. Use quando a repetição é sua - um job que caiu e foi reexecutado, uma fila que reentrega.
mem.add("she prefers tea", "alice", idempotency_key=f"import:{row.id}")
import:row-42), nunca uma constante: uma chave reutilizada em duas escritas diferentes repete a primeira e a segunda se perde em silêncio. Formato: 1-128 caracteres de [A-Za-z0-9._:-].with_timeout / with_retries
Ajuste um único ponto de chamada sem mexer no cliente já construído: um clone com timeout maior para um backfill grande, ou sem retentativas dentro do seu próprio laço de retry.
mem.with_timeout(120).add_bulk(big_blob, "alice") # this slow call only mem.with_retries(0).add("...", "alice") # you retry, not the SDK
TypeScript - todos os métodos, três grupos.
Escrever, ler, excluir. Todos os exemplos abaixo foram executados contra a API em produção em 2026-08-01; as respostas estão na íntegra.
npm install wontopos
import { Client } from "wontopos"; const mem = new Client({ apiKey: "wos-live-..." });
Escolha um modelo
A chave de API escolhe qual memória (sua conta); o modelo escolhe qual motor a lê. Todos os modelos compartilham uma mesma memória, então você pode armazenar com um e recuperar com outro. Defina um padrão no construtor; sobrescreva uma chamada específica com withModel().
const mem = new Client({ apiKey: "wos-live-...", model: "tablet-1" }); // default mem.recall("...", "alice"); // tablet-1 mem.withModel("scroll-1").recall("...", "alice"); // or pick a model per call
listModels
O catálogo - os ids que você pode passar em model e se cada um está disponível. Modelos com memory: "shared" leem o mesmo store; "isolated" mantém o seu próprio. Não requer chave de API.
await mem.listModels();
[{"id": "tablet-1", "name": "Tablet 1", "available": true, "memory": "shared"},
{"id": "tablet-2", "name": "Tablet 2", "available": true, "memory": "shared"},
{"id": "scroll-1", "name": "Scroll 1", "available": true, "memory": "shared"},
{"id": "scroll-1.2", "name": "Scroll 1.2", "available": true, "memory": "shared"}]ping
Confirme a conexão e que sua chave de API funciona: uma verificação de uma linha.
await mem.ping(); // true, or throws AuthenticationError / PaymentRequiredError
O catálogo acima sempre reflete os modelos disponíveis neste momento - passe qualquer outro id e você recebe um erro claro. Novos modelos aparecem nele automaticamente quando são lançados.
Escrever
add
Armazena uma memória. Embeddings gerados na entrada - sem chamada de LLM, você paga apenas embeddings.
await mem.add("she prefers tea over coffee", "alice"); await mem.add("I promised the summary by Friday", "alice", { speaker: "me" }); // its own words - no registration needed
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}addTurn
Armazena um turno de conversa (usuário + assistente) na memória de curto e de longo prazo de uma só vez.
await mem.addTurn("hi", "hello!", "alice");
{"status": "ok"}speaker
Cada memória pode carregar quem a disse. Registre uma pessoa uma vez e depois passe o nome como speaker; "me" (as palavras do próprio assistente) nunca precisa de registro. A busca também aceita speaker, para lembrar só as palavras de uma pessoa.
await mem.addSpeaker("Bob", "alice"); // once per person; "me" needs no registration await mem.add("I promised to send the report on Friday", "alice", { speaker: "me" }); await mem.add("Bob said the deadline moved to Tuesday", "alice", { speaker: "Bob" }); await mem.search("what did Bob say about deadlines?", "alice", 10, { speaker: "Bob" });
addBulk
Faz backfill de um grande bloco de texto. Fragmentado e convertido em embeddings no servidor - ideal para importar um histórico existente.
await mem.addBulk("Alice moved to Brooklyn in March. She works at a design studio downtown.", "alice");
{"elapsed_secs": 0.154154944, "status": "ok", "stored": 1, "total_chunks": 1}update
Um fato mudou. A memória antiga é marcada como substituída (mantida para histórico); a nova toma o lugar dela no recall.
await mem.update("576700aa-...", "she switched to coffee this year", "alice");
{"new_memory_id": "07e94433-b7cc-4e49-8d8f-f37fc1a392b7",
"old_memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "superseded"}Ler
search
Busca semântica, os mais relevantes primeiro. Puramente embeddings - sem correspondência por palavras-chave, então qualquer idioma encontra qualquer memória. O SDK retorna o array de memórias diretamente; o corpo HTTP bruto é mostrado abaixo. Num modelo com faixa própria (Scroll 1.2+) o serviço responde em duas faixas e o SDK devolve ambas fundidas, pelo que o array pode conter MAIS do que max_results. Dimensione a janela do prompt pelo que recebe, não pelo número pedido.
const r = await mem.search("what does she drink?", "alice", 1);
[{
"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624",
"content": "she prefers tea over coffee",
"category": "general",
"time_bucket": "2026-06",
"importance": 0.3,
"similarity": 0.6316057443618774,
"is_superseded": false,
"superseded_by": null,
"created_at": "2026-07-10T04:20:39.688276876Z"
}]| Campo | Significado |
|---|---|
| similarity | Similaridade bruta de embedding com a sua consulta (0–1). |
| is_superseded | Verdadeiro se este fato foi substituído por update(). |
| search_ms | Tempo de recuperação no servidor. |
recall
Uma única ida e volta retorna tudo o que o seu LLM precisa - cole o resultado direto no seu prompt: um contexto limitado e de tamanho fixo, não importa quanto você tenha armazenado.
const ctx = await mem.recall("what does she drink?", "alice");
{"short_term": {"count": 2, "turns": [{"role": "user", "content": "hi", ...}]},
"long_term": {"count": 4, "memories": [{"content": "she prefers tea over coffee",
"similarity": 0.63, ...}]},
"context": {"count": 4, "around_top_memory": [
"[match] she prefers tea over coffee",
"[after] Alice moved to Brooklyn in March. ..."]},
"instruction": "Use short_term for recent context, long_term for relevant
past memories, context for surrounding conversation of the
most relevant memory."}history
Turnos recentes da conversa (memória de curto prazo), do mais antigo para o mais recente.
const turns = await mem.history("alice");
{"count": 2, "turns": [
{"role": "user", "content": "hi", "timestamp": "2026-07-10T04:20:40.989011337Z"},
{"role": "assistant", "content": "hello!", "timestamp": "2026-07-10T04:20:40.989013416Z"}
], "user_id": "alice"}stats
Contagens de memórias de um usuário.
await mem.stats("alice");
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}get
Busca uma memória pelo id - o id que add ou list_memories retornou. Apenas o texto original armazenado e seus metadados, nunca o vetor. Um id de outro store, ou uma memória apagada ou invalidada, retorna 404.
const m = await mem.get("alice", "576700aa-...");
{"id": "576700aa-...", "content": "she prefers tea over coffee",
"category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
"is_superseded": false, "superseded_by": null}listMemories
Lista as memórias de um armazenamento - apenas o texto original que você guardou e seus metadados, nunca o vetor. Paginado por cursor: reenvie o next_cursor retornado para a próxima página.
const page = await mem.listMemories("alice", { limit: 100 });
{"count": 2, "next_cursor": null, "memories": [
{"id": "576700aa-...", "content": "she prefers tea over coffee",
"category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
"is_superseded": false, "superseded_by": null}
]}iterMemories · exportMemories
Percorra todas as memórias sem gerenciar o cursor, ou traga o armazenamento inteiro de uma vez.
for await (const m of mem.iterMemories("alice")) console.log(m.id, m.content); const everything = await mem.exportMemories("alice");
Excluir
delete
Exclui uma única memória pelo id.
await mem.delete("alice", "576700aa-...");
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}deleteAll
Apaga tudo de um usuário - uma única chamada, pronta para o GDPR.
await mem.deleteAll("alice");
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}Erros e confiabilidade
Cada falha é um erro tipado - capture o específico (limite de taxa, autenticação, pagamento) ou todos com o WosError base.
import { NotFoundError, PaymentRequiredError } from "wontopos"; try { await mem.add("...", "alice"); } catch (e) { if (e instanceof NotFoundError) await mem.createStore("alice"); else if (e instanceof PaymentRequiredError) topUp(); // out of credit else throw e; }
rateLimit
Leia a cota restante após qualquer chamada e desacelere antes de bater no limite.
await mem.search("...", "alice"); const rl = mem.rateLimit; // { limit: 150, remaining: 3, reset: ... }
searchSelf
As duas trilhas em uma única chamada num modelo de memória própria (Scroll 1.2+): o que os outros disseram e as palavras do PRÓPRIO agente - separadas, para que quem lê nunca confunda quem falou.
const { memories, self_memories } = await mem.searchSelf("what did I promise?", "alice"); // memories = what others said · self_memories = the agent's OWN words
listEngrams
Pergunte ao serviço quais engramas e formas de entrega o modelo selecionado consegue executar, em vez de fixar nomes que ficam desatualizados assim que um novo é lançado.
const { engrams, forms } = await mem.listEngrams(); // ask, never hard-code
filters
Restringe a busca a parte do repositório. Aplicado antes da ordenação, então você recebe as melhores correspondências dentro do filtro - não um top-N filtrado depois.
await mem.search("what did we decide", "alice", 10, { filters: { categories: ["work"], event_from: "2026-01-01" }, // when it HAPPENED });
idempotencyKey
Torna seguro repetir uma escrita. Use quando a repetição é sua - um job que caiu e foi reexecutado, uma fila que reentrega.
await mem.add("she prefers tea", "alice", {}, { idempotencyKey: `import:${row.id}` });
import:row-42), nunca uma constante: uma chave reutilizada em duas escritas diferentes repete a primeira e a segunda se perde em silêncio. Formato: 1-128 caracteres de [A-Za-z0-9._:-].withTimeout / withRetries
Ajuste um único ponto de chamada sem mexer no cliente já construído: um clone com timeout maior para um backfill grande, ou sem retentativas dentro do seu próprio laço de retry.
await mem.withTimeout(120_000).addBulk(bigBlob, "alice"); // this slow call only await mem.withRetries(0).add("...", "alice"); // you retry, not the SDK
Rust - todos os métodos, três grupos.
Escrever, ler, excluir. Todos os exemplos abaixo foram executados contra a API em produção em 2026-08-01; as respostas estão na íntegra.
cargo add wontopos
use wontopos::Client; let mem = Client::new("wos-live-...");
Escolha um modelo
A chave de API escolhe qual memória (sua conta); o modelo escolhe qual motor a lê. Todos os modelos compartilham uma mesma memória, então você pode armazenar com um e recuperar com outro. Defina um padrão com with_model(); encadeie-o novamente para sobrescrever uma chamada específica.
let mem = Client::new("wos-live-...").with_model("tablet-1"); // default mem.recall("...", "alice").await?; // tablet-1 mem.with_model("scroll-1").recall("...", "alice").await?; // or pick a model per call
list_models
O catálogo - os ids que você pode passar em with_model e se cada um está disponível. Modelos com memory: "shared" leem o mesmo store; "isolated" mantém o seu próprio. Não requer chave de API.
mem.list_models().await?;
[{"id": "tablet-1", "name": "Tablet 1", "available": true, "memory": "shared"},
{"id": "tablet-2", "name": "Tablet 2", "available": true, "memory": "shared"},
{"id": "scroll-1", "name": "Scroll 1", "available": true, "memory": "shared"},
{"id": "scroll-1.2", "name": "Scroll 1.2", "available": true, "memory": "shared"}]ping
Confirme a conexão e que sua chave de API funciona: uma verificação de uma linha.
mem.ping().await?; // Ok(true), or Err whose .kind() is Auth / PaymentRequired
O catálogo acima sempre reflete os modelos disponíveis neste momento - passe qualquer outro id e você recebe um erro claro. Novos modelos aparecem nele automaticamente quando são lançados.
Escrever
add
Armazena uma memória. Embeddings gerados na entrada - sem chamada de LLM, você paga apenas embeddings.
mem.add("she prefers tea over coffee", "alice", json!({})).await?; mem.add("I promised the summary by Friday", "alice", json!({"speaker": "me"})).await?; // its own words - no registration needed
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}add_turn
Armazena um turno de conversa (usuário + assistente) na memória de curto e de longo prazo de uma só vez.
mem.add_turn("hi", "hello!", "alice").await?;
{"status": "ok"}speaker
Cada memória pode carregar quem a disse. Registre uma pessoa uma vez e depois passe o nome como speaker; "me" (as palavras do próprio assistente) nunca precisa de registro. A busca também aceita speaker, para lembrar só as palavras de uma pessoa.
mem.add_speaker("Bob", "alice").await?; // once per person; "me" needs no registration mem.add("I promised to send the report on Friday", "alice", json!({"speaker": "me"})).await?; mem.add("Bob said the deadline moved to Tuesday", "alice", json!({"speaker": "Bob"})).await?; mem.search_with("what did Bob say about deadlines?", "alice", 10, json!({"speaker": "Bob"})).await?;
add_bulk
Faz backfill de um grande bloco de texto. Fragmentado e convertido em embeddings no servidor - ideal para importar um histórico existente.
mem.add_bulk("Alice moved to Brooklyn in March...", "alice", "general").await?;
{"elapsed_secs": 0.154154944, "status": "ok", "stored": 1, "total_chunks": 1}update
Um fato mudou. A memória antiga é marcada como substituída (mantida para histórico); a nova toma o lugar dela no recall.
mem.update("576700aa-...", "she switched to coffee this year", "alice").await?;
{"new_memory_id": "07e94433-b7cc-4e49-8d8f-f37fc1a392b7",
"old_memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "superseded"}Ler
search
Busca semântica, os mais relevantes primeiro. Puramente embeddings - sem correspondência por palavras-chave, então qualquer idioma encontra qualquer memória. O SDK retorna o array de memórias diretamente; o corpo HTTP bruto é mostrado abaixo. Num modelo com faixa própria (Scroll 1.2+) o serviço responde em duas faixas e o SDK devolve ambas fundidas, pelo que o array pode conter MAIS do que max_results. Dimensione a janela do prompt pelo que recebe, não pelo número pedido.
let r = mem.search("what does she drink?", "alice", 1).await?;
[{
"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624",
"content": "she prefers tea over coffee",
"category": "general",
"time_bucket": "2026-06",
"importance": 0.3,
"similarity": 0.6316057443618774,
"is_superseded": false,
"superseded_by": null,
"created_at": "2026-07-10T04:20:39.688276876Z"
}]| Campo | Significado |
|---|---|
| similarity | Similaridade bruta de embedding com a sua consulta (0–1). |
| is_superseded | Verdadeiro se este fato foi substituído por update(). |
| search_ms | Tempo de recuperação no servidor. |
recall
Uma única ida e volta retorna tudo o que o seu LLM precisa - cole o resultado direto no seu prompt: um contexto limitado e de tamanho fixo, não importa quanto você tenha armazenado.
let ctx = mem.recall("what does she drink?", "alice").await?;
{"short_term": {"count": 2, "turns": [{"role": "user", "content": "hi", ...}]},
"long_term": {"count": 4, "memories": [{"content": "she prefers tea over coffee",
"similarity": 0.63, ...}]},
"context": {"count": 4, "around_top_memory": [
"[match] she prefers tea over coffee",
"[after] Alice moved to Brooklyn in March. ..."]},
"instruction": "Use short_term for recent context, long_term for relevant
past memories, context for surrounding conversation of the
most relevant memory."}history
Turnos recentes da conversa (memória de curto prazo), do mais antigo para o mais recente.
let turns = mem.history("alice").await?;
{"count": 2, "turns": [
{"role": "user", "content": "hi", "timestamp": "2026-07-10T04:20:40.989011337Z"},
{"role": "assistant", "content": "hello!", "timestamp": "2026-07-10T04:20:40.989013416Z"}
], "user_id": "alice"}stats
Contagens de memórias de um usuário.
mem.stats("alice").await?;
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}get
Busca uma memória pelo id - o id que add ou list_memories retornou. Apenas o texto original armazenado e seus metadados, nunca o vetor. Um id de outro store, ou uma memória apagada ou invalidada, retorna 404.
let m = mem.get("alice", "576700aa-...").await?;
{"id": "576700aa-...", "content": "she prefers tea over coffee",
"category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
"is_superseded": false, "superseded_by": null}list_memories
Lista as memórias de um armazenamento - apenas o texto original que você guardou e seus metadados, nunca o vetor. Paginado por cursor: reenvie o next_cursor retornado para a próxima página.
mem.list_memories("alice", 100, None).await?;
{"count": 2, "next_cursor": null, "memories": [
{"id": "576700aa-...", "content": "she prefers tea over coffee",
"category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
"is_superseded": false, "superseded_by": null}
]}list_all_memories
Percorra todas as memórias sem gerenciar o cursor, ou traga o armazenamento inteiro de uma vez.
let all = mem.list_all_memories("alice").await?; // every page, collected
Excluir
delete
Exclui uma única memória pelo id.
mem.delete("alice", "576700aa-...").await?;
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}delete_all
Apaga tudo de um usuário - uma única chamada, pronta para o GDPR.
mem.delete_all("alice").await?;
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}Erros e confiabilidade
Cada falha é um erro tipado - capture o específico (limite de taxa, autenticação, pagamento) ou todos com o WosError base.
use wontopos::ErrorKind; match mem.search("...", "alice", 10).await { Ok(hits) => { /* use hits */ } Err(e) if e.kind() == ErrorKind::NotFound => { mem.create_store("alice").await?; } Err(e) if e.is_rate_limited() => { /* back off */ } Err(e) => return Err(e), }
rate_limit
Leia a cota restante após qualquer chamada e desacelere antes de bater no limite.
mem.search("...", "alice", 10).await?; let rl = mem.rate_limit(); // Some(RateLimit { remaining: Some(3), .. })
search_self
As duas trilhas em uma única chamada num modelo de memória própria (Scroll 1.2+): o que os outros disseram e as palavras do PRÓPRIO agente - separadas, para que quem lê nunca confunda quem falou.
let r = mem.search_self("what did I promise?", "alice", 10).await?; // r.memories = what others said · r.self_memories = the agent's OWN words
list_engrams
Pergunte ao serviço quais engramas e formas de entrega o modelo selecionado consegue executar, em vez de fixar nomes que ficam desatualizados assim que um novo é lançado.
let cat = mem.list_engrams().await?; // ask, never hard-code
filters
Restringe a busca a parte do repositório. Aplicado antes da ordenação, então você recebe as melhores correspondências dentro do filtro - não um top-N filtrado depois.
mem.search_with("what did we decide", "alice", 10, json!({"filters": { "categories": ["work"], "event_from": "2026-01-01" // when it HAPPENED }})).await?;
add_idempotent
Torna seguro repetir uma escrita. Use quando a repetição é sua - um job que caiu e foi reexecutado, uma fila que reentrega.
mem.add_idempotent("she prefers tea", "alice", json!({}), &format!("import:{}", row.id)).await?;
import:row-42), nunca uma constante: uma chave reutilizada em duas escritas diferentes repete a primeira e a segunda se perde em silêncio. Formato: 1-128 caracteres de [A-Za-z0-9._:-].with_timeout / with_retries
Ajuste um único ponto de chamada sem mexer no cliente já construído: um clone com timeout maior para um backfill grande, ou sem retentativas dentro do seu próprio laço de retry.
mem.with_timeout(120).add_bulk(big_blob, "alice", "general").await?; mem.with_retries(0).add("...", "alice", json!({})).await?;
curl - sem instalação, os mesmos métodos.
Nenhum SDK para instalar - qualquer cliente HTTP funciona. Defina sua chave uma vez e chame os mesmos endpoints que os SDKs encapsulam. URL base https://api.wontopos.com, autenticação via X-API-Key, JSON na entrada e na saída.
# set your key once (never hard-code it) export WOS_API_KEY="wos-live-..."
Escrever
store
Armazena uma memória. Embeddings gerados na entrada - sem chamada de LLM.
curl -X POST https://api.wontopos.com/api/v1/memory/store \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","content":"she prefers tea over coffee"}'
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}store-turn
Armazena um turno de conversa (usuário + assistente) na memória de curto e de longo prazo de uma só vez.
curl -X POST https://api.wontopos.com/api/v1/memory/store-turn \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","user_msg":"hi","assistant_msg":"hello!"}'
{"status": "ok"}speaker
Cada memória pode carregar quem a disse. Registre uma pessoa uma vez e depois passe o nome como speaker; "me" (as palavras do próprio assistente) nunca precisa de registro. A busca também aceita speaker, para lembrar só as palavras de uma pessoa.
curl -X POST https://api.wontopos.com/api/v1/memory/speakers \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","speaker":"Bob"}' # once per person curl -X POST https://api.wontopos.com/api/v1/memory/store \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","content":"I promised to send the report on Friday","metadata":{"speaker":"me"}}' curl -X POST https://api.wontopos.com/api/v1/memory/store \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","content":"Bob said the deadline moved to Tuesday","metadata":{"speaker":"Bob"}}' curl -X POST https://api.wontopos.com/api/v1/memory/search \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","query":"what did Bob say about deadlines?","speaker":"Bob"}'
supersede
Um fato mudou - a memória antiga é marcada como substituída, a nova toma o lugar dela no recall.
curl -X POST https://api.wontopos.com/api/v1/memory/supersede \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","old_memory_id":"576700aa-...","new_content":"she switched to coffee this year"}'
{"new_memory_id": "07e94433-...", "old_memory_id": "576700aa-...", "status": "superseded"}bulk-store
Carrega um histórico longo em uma chamada - fatiado e incorporado no servidor.
curl -X POST https://api.wontopos.com/api/v1/memory/bulk-store \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","content":"...a long history...","category":"general"}'
{"elapsed_secs": 0.138589761, "status": "ok", "stored": 1, "total_chunks": 1}Idempotency-Key
Torna seguro repetir uma escrita. Use quando a repetição é sua - um job que caiu e foi reexecutado, uma fila que reentrega.
# same key + same body = the FIRST response is replayed, nothing is stored twice curl -X POST https://api.wontopos.com/api/v1/memory/store \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -H "Idempotency-Key: import:row-42" \ -d '{"user_id":"alice","content":"she prefers tea over coffee"}'
import:row-42), nunca uma constante: uma chave reutilizada em duas escritas diferentes repete a primeira e a segunda se perde em silêncio. Formato: 1-128 caracteres de [A-Za-z0-9._:-].Ler
search
Busca semântica, os mais relevantes primeiro. Puramente embeddings - qualquer idioma encontra qualquer memória.
curl -X POST https://api.wontopos.com/api/v1/memory/search \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","query":"what does she drink?","max_results":1}'
{"memories": [{"id": "576700aa-...", "content": "she prefers tea over coffee",
"similarity": 0.63, "is_superseded": false}], "search_ms": 315, "total_found": 1}search + filters
Restringe a busca a parte do repositório. Aplicado antes da ordenação, então você recebe as melhores correspondências dentro do filtro - não um top-N filtrado depois.
curl -X POST https://api.wontopos.com/api/v1/memory/search \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","query":"what did we decide", "filters":{"categories":["work"],"event_from":"2026-01-01","event_to":"2026-06-30"}}'
get
Lê uma memória pelo id que store ou list devolveu - texto original e metadados, sem vetores.
curl -X POST https://api.wontopos.com/api/v1/memory/get \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","memory_id":"576700aa-f0e0-4c26-99a0-10e2d5b0d624"}'
list
Percorre tudo em um repositório, cursor a cursor. Serve para navegar ou exportar.
curl -X POST https://api.wontopos.com/api/v1/memory/list \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","limit":100}' # pass next_cursor back for the next page
{"count": 3, "memories": [{"id": "1a1cfc47-...", "content": "...", "category": "general",
"created_at": "2026-07-31T18:04:51.937117314+00:00", "event_date": null, "is_superseded": false}],
"next_cursor": "722c08e5-8998-4882-979e-d71995b5b4af", "user_id": "docs_livetest"}recall
Uma única ida e volta retorna curto prazo + longo prazo + contexto + uma instrução. Cole direto no seu prompt.
curl -X POST https://api.wontopos.com/api/v1/memory/recall \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","query":"what does she drink?"}'
{"short_term": {"count": 2, "turns": [...]},
"long_term": {"count": 4, "memories": [{"content": "she prefers tea over coffee", "similarity": 0.63}]},
"context": {"count": 4, "around_top_memory": ["[match] she prefers tea over coffee"]},
"instruction": "Use short_term for recent context, long_term for relevant past memories..."}Excluir
forget
Exclui uma memória pelo id, ou omita-o para excluir tudo de um usuário (GDPR).
curl -X POST https://api.wontopos.com/api/v1/memory/forget \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice"}' # omit memory_id = delete all
{"memories_deleted": 1, "status": "deleted", "user_id": "alice"}Todos os endpoints + campos do corpo →
Variantes exclusivas do Rust
Python e TypeScript recebem esses valores como argumentos opcionais. O Rust estável não tem argumentos padrão nem nomeados, então cada um é um método próprio, e não um builder que você precisa finalizar.
mem.add_with(text, None, json!({}), extra) // add + extra body fields
mem.search_opts(q, None, 10, &opts) // search + verify / max_images
mem.search_with(q, None, 10, extra) // search + any other field
mem.recall_with(q, None, extra) // recall + extra
mem.search_self_with(q, None, 10, extra) // self lane + extra
mem.engram_with(name, q, None, extra) // engram + extra
mem.update_idempotent(old, new, None, key) // update + Idempotency-Key
mem.add_turn_idempotent(u, a, None, key)
mem.add_bulk_idempotent(text, None, cat, key)
mem.revisions_page(None, "revised", 20, None, None)
mem.list_all_images(None, None) // = iter_images, collectedlist_all_images também é exportado como iter_images, o mesmo nome usado pelos outros dois SDKs - quem chega daquela documentação digita esse nome primeiro.
Engrams
Ferramentas de recall invocáveis pelo seu modelo - cada uma é uma estratégia de recuperação diferente sobre a mesma memória. Use uma, ou execute várias ao mesmo tempo.
Novos engrams são lançados regularmente - esta lista cresce.
Memoir & Archive Scroll 1.2+
Esta é uma forma de entrega, não uma ferramenta invocável. No Scroll 1.2 e superiores, escolha por chamada - form: "memoir" ou form: "archive" - e esse recall, incluindo uma busca simples, volta com o tempo escrito dessa forma.
Time_awareness Scroll 1.2+
Uma forma de entrega - escolhida por chamada. Passe form - memoir ou archive - em qualquer chamada de um modelo compatível (Scroll 1.2 e superiores), e a resposta volta renderizada dessa forma: uma busca simples, um recall ou qualquer engram. Nos SDKs é um campo form, como tz; via HTTP é o cabeçalho X-WOS-Form. Um Memoir se lê do jeito que uma pessoa lembra; um Archive mantém um registro exato - a diferença aparece principalmente em como cada um escreve o tempo.
Memoir
form: "memoir"Conta o que aconteceu e como um momento levou ao seguinte, com a noção suave de tempo que uma pessoa recorda - lido como experiência, não como uma lista.
Archive
form: "archive"Retorna as correspondências como registros exatos - tempo decorrido preciso e âncoras absolutas, estruturados para um modelo ler diretamente.
store / add sob um user_id (esse user_id é o store daquela pessoa). Armazene primeiro; depois qualquer recall - incluindo a busca simples abaixo - volta com marcação de tempo. Veja o Início rápido para armazenar.# the memoir form on a plain search — and on recall, the LLM's one-call context
r = mem.search("what does Alice drink?", user_id="alice", model="scroll-1.2", form="memoir", tz=9)
ctx = mem.recall("what does Alice drink?", user_id="alice", model="scroll-1.2", form="memoir", tz=9)
# every memory's .time reads "a couple weeks ago" (archive → "2 weeks ago (Jun 09)") — the LLM sees human timetz é o deslocamento UTC de quem chama, em horas - assim, "esta manhã" e o limite de dia às 4h caem no horário local deles. Omita para UTC; via HTTP, é o cabeçalho X-WOS-Timezone. Aproximadamente, por região: Leste dos EUA -5, Centro dos EUA -6, Oeste dos EUA -8 · Reino Unido / Lisboa 0 · Europa Central +1 · Europa Oriental +2 · Índia +5.5 · China / Cingapura +8 · Coreia / Japão +9 · Sydney +10. (Horário padrão - o horário de verão desloca algumas regiões em +1; passe o que os seus usuários estiverem realmente usando.)
A mesma busca, duas formas - as memórias são idênticas, apenas o time muda:
{ "count": 3, "memories": [
{ "content": "Alice prefers tea over coffee", "time": "a couple weeks ago" },
{ "content": "met Alice at the cafe downtown", "time": "yesterday afternoon" },
{ "content": "Alice moved to Brooklyn", "time": "about half a year ago" }
] }{ "count": 3, "memories": [
{ "content": "Alice prefers tea over coffee", "time": "2 weeks ago (Jun 09)" },
{ "content": "met Alice at the cafe downtown", "time": "yesterday at 14:00" },
{ "content": "Alice moved to Brooklyn", "time": "6 months ago (Dec 2025)" }
] }| Decorrido | Memoir | Archive |
|---|---|---|
| 3 min | a few minutes ago | 3 minutes ago |
| 14 min | about 15 minutes ago | 14 minutes ago |
| 30 min | half an hour ago | 30 minutes ago |
| 50 min | about an hour ago | 50 minutes ago |
| 2 h | a couple hours ago | 2 hours ago, at 13:10 |
| 8 h | this morning | 8 hours ago, at 07:10 |
| ontem à tarde | yesterday afternoon | yesterday at 14:00 |
| ontem à noite | last night | 17 hours ago, at 22:00 |
| 2 dias | a couple days ago | 2 days ago (Tue 15:10) |
| 6 dias | several days ago | 6 days ago (Fri 15:10) |
| 9 dias | about a week ago | last week (Jun 16) |
| 16 dias | a couple weeks ago | 2 weeks ago (Jun 09) |
| 35 dias | about a month ago | last month (May 21) |
| 60 dias | a couple months ago | 2 months ago (Apr 2026) |
| 180 dias | about half a year ago | 6 months ago (Dec 2025) |
| 380 dias | about a year ago | last year (Jun 2025) |
| 800 dias | a couple years ago | 2 years ago (Apr 2024) |
| 1500 dias | about 4 years ago | 4 years ago (May 2022) |
Todos os valores acima são a saída real do renderizador. Observe as duas linhas de "ontem": um Memoir separa a tarde da noite anterior - um dia é um sono - enquanto um Archive escreve um único horário de relógio e não traça linha entre dia e noite.
Como cada modo lê o tempo
Memoir - do jeito que as pessoas realmente falam. Momentos recentes permanecem razoavelmente nítidos (uns 15 minutos, meia hora), e depois a formulação vai se alargando quanto mais para trás se vai - algumas semanas, cerca de meio ano, uns dois anos - do mesmo jeito que a própria memória se afrouxa com a distância. Dentro de um dia, ele troca o relógio por um marco: esta manhã, ontem à noite, ontem à tarde. E um dia é um sono, não um tique de calendário: o limite fica por volta das 4h no horário local, então uma madrugada ainda é lida como a mesma noite, não como o dia seguinte.
Archive - preciso, sempre com uma âncora. Cada linha traz o tempo decorrido exato mais uma referência absoluta a partir da qual um modelo pode calcular, e a âncora fica mais precisa conforme se aproxima do presente: um horário de relógio para hoje (8 horas atrás, às 07:10), um dia da semana e horário nesta semana (2 dias atrás (ter 15:10)), uma data neste mês (semana passada (16 jun)), um mês e ano além disso (6 meses atrás (dez 2025)). Nunca vago, nunca errado.
deep_recall
Recall multi-hop. Busca a sua consulta, depois pega a melhor correspondência e busca de novo sobre o conteúdo dela - trazendo contexto vinculado que uma busca única deixaria passar. Ideal quando as memórias referenciam umas às outras (uma pessoa → seus projetos → detalhes). Retorna até ~12.
out = mem.engram("deep_recall", "what should I know about Alice?", user_id="alice"){ "engram": "deep_recall", "hops": 2, "count": 12,
"memories": [ ... ],
"usage": { "input_tokens": 200, "output_tokens": 589 } }usage (entrada + saída), contado pelo mesmo tokenizador do restante da API; sem taxa oculta por engram. Precisa de vários de uma vez? Chame-os simultaneamente - cada engram é uma requisição independente.timeline
Recall em ordem temporal. Retorna memórias ordenadas do mais recente para o mais antigo por quando o evento aconteceu, não por relevância. Para perguntas de "quando foi X", histórico e sequência. Retorna até 15.
events = mem.engram("timeline", "project milestones", user_id="alice"){ "engram": "timeline", "hops": 1, "count": 15,
"memories": [ ... ],
"usage": { "input_tokens": 100, "output_tokens": 736 } }usage (entrada + saída), contado pelo mesmo tokenizador do restante da API; sem taxa oculta por engram. Precisa de vários de uma vez? Chame-os simultaneamente - cada engram é uma requisição independente.gather
Coleta ampla. Busca e depois expande ao redor das três melhores correspondências - uma rede mais ampla que o deep_recall. Use-o para trazer tudo relacionado a uma pessoa, projeto ou tópico em uma única chamada. Retorna até ~18.
related = mem.engram("gather", "everything about Project Atlas", user_id="alice"){ "engram": "gather", "hops": 4, "count": 18,
"memories": [ ... ],
"usage": { "input_tokens": 400, "output_tokens": 637 } }usage (entrada + saída), contado pelo mesmo tokenizador do restante da API; sem taxa oculta por engram. Precisa de vários de uma vez? Chame-os simultaneamente - cada engram é uma requisição independente.equilibrium
Correção de desvio. A busca semântica estreita-se ao longo de uma sessão: a consulta carrega o estado atual, por isso traz memórias do mesmo estado e o turno seguinte inclina-se ainda mais para esse lado. Este engrama alarga o resultado por três eixos que a consulta não controla: a dispersão no tempo, a associação que se afasta da consulta e a parte substancial do armazém. Use quando as respostas começarem a repetir-se ou a ficar planas. Para um facto específico, deep_recall ou gather servem melhor, porque ficam mais perto da consulta. Devolve até 12.
wide = mem.engram("equilibrium", "how have things been lately?", user_id="alice"){ "engram": "equilibrium", "hops": 3, "count": 12,
"memories": [ ... ],
"usage": { "input_tokens": 300, "output_tokens": 293 } }usage (entrada + saída), contado pelo mesmo tokenizador do restante da API; sem taxa oculta por engram. Precisa de vários de uma vez? Chame-os simultaneamente - cada engram é uma requisição independente.tone_stabilizer
A própria voz. Sessões longas afastam um assistente do seu registo: as respostas alongam-se, viram relatório ou assumem o humor do último trecho. A auto-recordação comum agrava isso, porque acompanha o estado atual e devolve as falas mais recentes como se fossem o carácter. Este engrama devolve, em vez disso, as próprias palavras de antes desse trecho. Precisa de turnos guardados com speaker me; sem nenhum, devolve vazio em vez de adivinhar. Devolve até 10.
# store the assistant's turns as speaker "me", then pull its own register back
mem.add("I keep answers short unless you ask for detail.", user_id="alice", speaker="me")
mine = mem.engram("tone_stabilizer", "how do I usually answer?", user_id="alice"){ "engram": "tone_stabilizer", "hops": 2, "count": 10,
"memories": [ { "content": "I keep answers short unless you ask for detail.", "speaker": "me" }, ... ],
"usage": { "input_tokens": 200, "output_tokens": 442 } }usage (entrada + saída), contado pelo mesmo tokenizador do restante da API; sem taxa oculta por engram. Precisa de vários de uma vez? Chame-os simultaneamente - cada engram é uma requisição independente.Todos os endpoints, uma única URL base.
Nenhum SDK necessário - qualquer cliente HTTP funciona. URL base https://api.wontopos.com, autenticação via o cabeçalho X-API-Key, JSON na entrada e na saída. As operações de memória são POST; gerenciar stores usa POST / GET / DELETE em /collection. Um store precisa existir primeiro (veja Stores), ou as operações dentro do store retornam 404.
Cabeçalhos
| Cabeçalho | O que faz |
|---|---|
| X-API-Key | Obrigatório em toda chamada. Sua chave, emitida no console. |
| X-WOS-Model | Opcional. Qual motor responde. Omita e o padrão da conta é usado. GET /api/v1/models lista os modelos que a sua chave pode selecionar; um endpoint que um motor mais antigo não consegue atender responde 501 e informa esse modelo. |
| Idempotency-Key | Opcional, nas escritas. A mesma chave com o mesmo corpo repete a primeira resposta em vez de armazenar de novo - veja a nota abaixo. |
Endpoint
| Endpoint | Finalidade | Campos do corpo |
|---|---|---|
| POST /api/v1/memory/collection | criar um store | user_id |
| GET /api/v1/memory/collections | listar seus stores | (nenhum) |
| DELETE /api/v1/memory/collection | excluir um store + suas memórias | user_id |
| /api/v1/memory/store | armazenar uma memória | user_id · content · metadata? (event_date · speaker) · image? |
| /api/v1/memory/store-turn | armazenar um turno de conversa | user_id · user_msg · assistant_msg |
| POST /api/v1/memory/speakers | registrar um falante (explícito, até 50) | user_id · speaker |
| GET /api/v1/memory/speakers | listar falantes registrados + contagens | user_id |
| DELETE /api/v1/memory/speakers | remover um falante (memórias ficam) | user_id · speaker |
| /api/v1/memory/by-speaker | o que uma pessoa disse, do mais recente ("me" = o agente) | user_id · speaker · limit? · before? · skip_ids? |
| POST /api/v1/memory/image | os bytes originais de uma memória com imagem | user_id · memory_id |
| DELETE /api/v1/memory/image | remover a imagem e manter a legenda | user_id · memory_id · preview? |
| /api/v1/memory/images | as imagens de um store, do mais recente (+ o total) | user_id · limit? · before? · skip_ids? |
| /api/v1/memory/lineage | a cadeia de edições de uma memória, do mais antigo | user_id · memory_id |
| /api/v1/won/revisions | quanto de um store foi reescrito. Grátis | user_id · include? · limit? · before? · skip_ids? |
| /api/v1/memory/revisions | a mesma chamada sob o plano memory. Grátis | user_id · include? · limit? · before? · skip_ids? |
| /api/v1/memory/bulk-store | backfill de um bloco de texto | user_id · content · category? · timestamp? |
| /api/v1/memory/search | busca semântica | user_id · query · max_results? · speaker? · cache_control? · filters? · verify? · max_images? |
| /api/v1/memory/recall | curto + longo + contexto | user_id · query · limit? · context_limit? |
| /api/v1/memory/get | uma memória por id | user_id · memory_id |
| /api/v1/memory/list | percorrer um repositório por páginas | user_id · limit? · cursor? |
| /api/v1/memory/history | turnos recentes | user_id |
| /api/v1/memory/stats | contagens de memórias | user_id |
| /api/v1/memory/supersede | substituir um fato que mudou | user_id · old_memory_id · new_content |
| /api/v1/memory/forget | excluir uma (ou todas) | user_id · memory_id? (omitir = excluir tudo) |
| GET /api/v1/engram | engramas que este modelo pode executar | (nenhum) |
| POST /api/v1/engram/run | executar um engrama | name · user_id · query · form? · tz? |
| GET /api/v1/models | modelos disponíveis | (nenhum) |
Idempotency-Key. A mesma chave com o mesmo corpo repete a primeira resposta em vez de armazenar de novo (10 minutos); a mesma chave com corpo diferente responde 422. Apenas 2xx são cacheados, portanto uma chamada que falhou pode ser repetida na hora.# create the store once (stores are explicit) curl -X POST https://api.wontopos.com/api/v1/memory/collection \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice"}' # store a memory curl -X POST https://api.wontopos.com/api/v1/memory/store \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","content":"she prefers tea over coffee"}' # recall - one call, ready for your prompt curl -X POST https://api.wontopos.com/api/v1/memory/recall \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","query":"what does alice drink?"}'
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}Os mesmos recursos para todos.
Os níveis só aumentam seus limites.
Todo nível executa o motor completo - mesma qualidade de recall, mesmos idiomas, todos os métodos. Os níveis avançam automaticamente até o Nível 5 conforme suas compras acumuladas de créditos crescem, sem solicitação nem contato com vendas. Enterprise (Nível 6) é a única exceção.
Limites de gasto
Cada nível limita quanto você pode gastar por mês-calendário. Você avança imediatamente quando suas compras acumuladas de créditos atingem o próximo patamar.
| Nível de uso | Compra de créditos | Limite mensal de gasto |
|---|---|---|
| Tier 1 | $5 | $100 |
| Tier 2 | $40 | $500 |
| Tier 3 | $200 | $1,000 |
| Tier 4 | $400 | $5,000 |
| Tier 5 | $1,000 | $25,000 |
| Tier 6 - Enterprise | Fale conosco | Sem limite |
Limites de requisições
Os limites de requisições são por conta - todas as chaves de API de uma conta compartilham um mesmo limite, que escala com o seu nível. Excedê-lo retorna um 429 com um cabeçalho retry-after; recue (1s → 2s → 4s) e tente novamente. Todos os endpoints são compatíveis com idempotência, então repetir requisições é seguro.
| Nível | Requisições por minuto |
|---|---|
| Tier 1 | 150 |
| Tier 2 | 300 |
| Tier 3 | 600 |
| Tier 4 | 1,500 |
| Tier 5 | 3,000 |
| Tier 6 - Enterprise | Personalizado |
O Enterprise (Nível 6) recebe limites de requisições personalizados, um SLA, suporte dedicado e uma licença opcional de self-host - fale conosco.
Chamadas gratuitas
Alguns endpoints não têm cobrança nenhuma - estão reunidos sob Won. No lugar de um preço, eles têm dois limites.
- 10 requisições por minuto, por endpoint. Cada endpoint gratuito mantém o próprio bucket, então consumir um não consome o outro.
- 300 requisições por hora, compartilhadas. Todos os endpoints gratuitos consomem uma única cota horária por conta.
Nenhum dos dois é atingido em uso comum, e nenhum deles afeta os limites pagos acima.
Quando algo dá errado.
Os erros retornam como um envelope JSON com um type estável, uma mensagem legível e um request_id que você pode nos enviar ao relatar um problema.
{"type": "error", "error": {
"type": "authentication_error",
"message": "Invalid or revoked API key.",
"request_id": "063f8b83-eee2-4383-a5cf-11e4bcd29d7c"
}}| HTTP | Significado | O que fazer |
|---|---|---|
| 400 | Corpo malformado (campo ausente ou de tipo errado) | A mensagem indica o campo exato - corrija e tente novamente. |
| 401 | Chave de API inválida ou revogada | Verifique a chave; emita uma nova no console. |
| 402 | Saldo esgotado, nenhum cartão cadastrado, ou limite do nível atingido | Adicione créditos ou cadastre um cartão no console. A resposta traz balance_cents e floor_cents, então você consegue saber qual dos dois barrou a chamada. |
| 404 | Memória, store ou imagem inexistente | Verifique o id. get_image também responde 404 quando a memória existe mas não carrega nenhuma imagem. |
| 409 | Esse nome já está em uso | Os nomes de store e de workspace são únicos dentro de uma conta - escolha outro. |
| 413 | Corpo da requisição acima de 10MB | O Base64 fica cerca de 33% maior que o arquivo que codifica, então redimensione a imagem antes de codificá-la. |
| 429 | Limite de requisições excedido | O SDK já tenta novamente por você, com recuo e jitter, respeitando o Retry-After. Receber um deles significa que as tentativas se esgotaram - reduza a sua concorrência em vez de envolver tudo em um laço próprio. |
| 501 | O motor deste modelo não implementa esse endpoint | Imagens e histórico de revisões exigem um motor mais recente. GET /api/v1/models lista quais modelos atendem o quê. |
| 5xx | Problema no servidor | Tente novamente com recuo, mas não às cegas. O SDK não repete automaticamente um 5xx aqui, porque toda chamada desta API é um POST e o servidor pode já ter armazenado a sua solicitação. Reenvie com uma chave de idempotência para que uma repetição não possa gravar duas vezes, e inclua o request_id se entrar em contato conosco. |
Todo erro é um WosError, e cada status também tem uma classe própria - BadRequestError, AuthenticationError, PaymentRequiredError, NotFoundError, ConflictError, RateLimitError, ServerError, APIConnectionError. Capture aquela que você pretende tratar em vez de comparar números.
# SDK error handling (Python) from wontopos import Client, WosError, RateLimitError, PaymentRequiredError try: mem.search("...", user_id="alice") except PaymentRequiredError: ... # 402 - top up except RateLimitError: ... # 429 - the SDK already retried; slow down except WosError as e: ... # e.status, e.message, e.request_id except (ValueError, TypeError): ... # never left the client
Alguns erros nunca chegam até nós. A chave de API, o id do store, a chave de idempotência e a imagem são todos verificados antes de a requisição sair, e nesses casos o que é levantado é ValueError ou TypeError - não WosError. Um except WosError sozinho não vai capturá-los.
Os limites de requisições são por conta, compartilhados entre todas as suas chaves, e escalam com o seu nível - veja Níveis de uso. O uso da sua conta é exibido no console.
lineage
A cadeia de edições por trás de uma memória, da mais antiga para a mais recente. revisions diz quanto um store mudou; esta chamada diz o que aconteceu com um fato.
revisions, esta é uma chamada cobrada normalmente, porque retorna conteúdo de memória.Passe o id de qualquer memória da cadeia. As versões substituídas são mantidas em vez de apagadas, então uma busca que retorna apenas o fato atual ainda pode ser rastreada até a origem.
chain = mem.lineage(memory_id=mid)["chain"]
for step in chain:
print(step["changed_at"], step["action"], step["content"]){ "memory_id": "m_9", "count": 3, "truncated": false,
"chain": [
{ "memory_id": "m_3", "content": "lives in Seoul",
"created_at": "2026-03-02T…", "changed_at": "2026-06-11T…",
"action": "replaced", "confidence": 0.94,
"superseded_by": "m_7", "is_current": false },
{ "memory_id": "m_7", "content": "moved to Busan", … },
{ "memory_id": "m_9", "content": "Haeundae, specifically",
"changed_at": null, "superseded_by": null, "is_current": true }
] }| Campo | O que faz |
|---|---|
| chain | As versões, da mais antiga para a mais recente. Cada uma carrega os mesmos campos de uma memória, mais os quatro abaixo. |
| changed_at | Quando esta versão foi substituída (RFC3339), ou null enquanto ela ainda está em vigor. |
| action | O que aconteceu neste elo - como a substituição se relacionou com esta versão. |
| confidence | Quanto o motor estava seguro dessa relação, de 0 a 1. |
| is_current | True para a única versão ainda em vigor. Exatamente uma por cadeia. |
| truncated | True quando a cadeia era mais longa do que o serviço percorre. Os passos retornados continuam sendo os mais antigos. |
Para que serve
Dois usos. Depuração: por que uma memória está redigida como está hoje. E permitir que um assistente veja o próprio histórico - um fato corrigido três vezes é um tipo de fato diferente de um escrito uma única vez, e só a cadeia mostra isso.
Won é para quem lê a memória.
A maior parte desta API responde com memória. Won responde sobre ela: quanto de um store foi reescrito e até onde dá para confiar nele. É voltado ao lado que lê, normalmente o assistente que você está construindo, e não à pessoa de quem as memórias falam.
Wontopos é Won + Topos, um único lugar onde a memória mora. Won é a parte desse lugar que informa sobre a memória em vez de devolvê-la. Estas chamadas são gratuitas, somente leitura, e nunca tocam na recuperação: perguntar não custa nada ao seu usuário e não muda nada do que está guardado.
O que está disponível hoje
Por enquanto, uma chamada.
| Chamada | O que faz |
|---|---|
| POST /won/revisions | Quanto deste store foi alterado desde que foi escrito. Dois números e duas frases que os explicam. |
Um exemplo prático
Use a proporção, não a contagem bruta. 3 de 40 e 30 de 40 pedem tratamentos diferentes.
r = mem.revisions()
# {"revised": 3, "total": 40, "counts": "…", "excludes": "…"}
if r["revised"] / r["total"] > 0.1:
system += "Some of what you remember here has been corrected since."counts e excludes são retornados como frases, não como flags, já que quem chama costuma ser um modelo. As exclusões não são contadas.
Preço e limites
| Regra | Valor |
|---|---|
| Preço | Nenhum. As chamadas gratuitas ignoram os controles de cobrança - sem cobrança de tokens, sem taxa por requisição e sem registro de uso. |
| Por minuto | 10 por minuto, por conta e por endpoint. Consumir o minuto de um endpoint não consome o de outro. |
| Por hora | 300 por hora, por conta, compartilhadas por todas as chamadas gratuitas. Esse limite ignora o caminho, então adicionar endpoints gratuitos não aumenta o total que uma conta pode gastar. |
| Em relação ao tráfego pago | Separados nas duas direções. Estas chamadas não podem deixar as suas buscas mais lentas e as suas buscas não podem esgotar estas chamadas. As chaves de uma mesma conta compartilham os buckets, então ter mais chaves não multiplica a cota. |
Os dois tetos respondem 429 com Retry-After em segundos e uma mensagem indicando qual deles foi atingido.
Retry-After: 41
{ "error": { "type": "rate_limit_error",
"message": "This endpoint is free and limited to 10 requests per
minute, counted per endpoint. Retry in 41s." } }/api/v1/memory/revisions, para clientes publicados antes de a superfície Won existir. É o mesmo handler e o mesmo orçamento, não uma segunda cota. Código novo deve usar o endereço Won.Quanto de um store foi reescrito
revisions responde revised de total: quantas memórias de um store foram alteradas depois de escritas. Vale a pena perguntar antes de se apoiar na memória para algo que importa, ou quando um fato lembrado não bate com o que o usuário está dizendo agora. Um store em que três de cada dez fatos foram substituídos merece menos confiança do que um que ninguém editou.
Contagens
Conta o que uma transformação tocou - substituído, atualizado, revogado e imagens removidas.
mem.revisions()
# {"revised": 3, "unrevised": 37, "total": 40, …}| Campo | O que significa |
|---|---|
| revised | Memórias que uma transformação tocou. |
| unrevised | Memórias que nada tocou desde que foram escritas. revised + unrevised é sempre igual a total - é um valor derivado, não contado à parte, então uma escrita concorrente não pode fazer os três discordarem. |
| total | Memórias que existem no store. |
| counts / excludes | Frases simples, não flags, explicitando o que os números cobrem. Quem chama costuma ser um modelo. |
Ler a lista
Passe include para obter as próprias memórias, não apenas a quantidade. Omita e você recebe só as contagens, que é a chamada barata.
page = mem.revisions(include="revised", limit=20)
page["memories"], page["matched"], page["has_more"]| Campo | O que faz |
|---|---|
| include | "revised" ou "unrevised". Qualquer outro valor é recusado com um 400 em vez de voltar para as contagens - um erro de digitação que descarta a lista em silêncio fica idêntico a um store vazio. |
| limit | De 5 a 20, padrão 20. Fora do intervalo, ou com o tipo errado, é recusado, não ajustado ao limite. |
| matched | Total de linhas por trás desta página, não o tamanho da página. |
| ordered_by | O serviço declara a própria ordenação: primeiro as armazenadas mais recentemente, não as editadas mais recentemente. |
| next_before | Cursor para a próxima página, junto com next_skip_ids. Devolva os dois; os ids se acumulam entre as páginas. |
revised baixo. Este número diz quanto foi reescrito, não quanto desapareceu.Além da janela de contexto.
O WOS recupera de históricos de 1.4M tokens - muito maiores que qualquer janela de contexto de LLM - e ainda assim devolve uma fatia enxuta de ~1,470 tokens.
A memória do seu agente não é limitada pelo que cabe em um prompt. Ela guarda tudo e recupera apenas o que importa, não importa o quanto o histórico cresça.
Privado, e seu.
Seus dados permanecem no seu store. Nunca treinamos com eles, os visualizamos ou os reutilizamos - apenas os organizamos para que você possa recuperá-los.
- BYOK. Sua chave de LLM é enviada a cada requisição e nunca armazenada.
- Isolado. As memórias têm escopo por conta e, depois, por
user_id. - Exclusão GDPR & self-host. Uma única chamada apaga um usuário; execute o motor no seu próprio ambiente, se preferir.