Add test files
This commit is contained in:
84
test_claude.py
Normal file
84
test_claude.py
Normal file
@ -0,0 +1,84 @@
|
||||
import anthropic
|
||||
import tomli
|
||||
import time
|
||||
from anthropic import APIError, APIConnectionError, RateLimitError
|
||||
|
||||
# Exponential backoff settings
|
||||
MAX_RETRIES = 5
|
||||
INITIAL_DELAY = 1 # seconds
|
||||
BACKOFF_FACTOR = 2
|
||||
|
||||
# def add_agent_entry():
|
||||
def add_user_entry(role: str, message:str):
|
||||
return
|
||||
|
||||
def add_system_entry():
|
||||
return
|
||||
|
||||
def test():
|
||||
with open('anthropic.toml', 'rb') as f:
|
||||
toml_anthropic = tomli.load(f)
|
||||
with open('test_task.toml', 'rb') as f:
|
||||
toml_task = tomli.load(f)
|
||||
|
||||
sonnet_3_7_latest = "claude-3-7-sonnet-20250219"
|
||||
|
||||
retry_count = 0
|
||||
delay = INITIAL_DELAY
|
||||
|
||||
client = anthropic.Anthropic(api_key=toml_anthropic['API']['API_KEY'])
|
||||
|
||||
while retry_count < MAX_RETRIES:
|
||||
try:
|
||||
response = client.messages.create(
|
||||
model=sonnet_3_7_latest,
|
||||
max_tokens=20000,
|
||||
temperature=0.8,
|
||||
system=[
|
||||
{
|
||||
"type": "text",
|
||||
# "text": toml_task['System']['content'],
|
||||
"text": toml_task['System']['content'],
|
||||
"cache_control": {"type": "ephemeral"}
|
||||
}
|
||||
],
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": toml_task['User']['background'],
|
||||
"cache_control": {"type": "ephemeral"}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": toml_task['User']['request'],
|
||||
"cache_control": {"type": "ephemeral"}
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
print(f"Usage: {response.usage}")
|
||||
with open('test_response.md', 'w', encoding='utf-8') as f:
|
||||
for content_block in response.content:
|
||||
f.write(content_block.text + '\n')
|
||||
return
|
||||
|
||||
except (APIError, APIConnectionError, RateLimitError) as e:
|
||||
print(f"Attempt {retry_count + 1} failed: {str(e)}")
|
||||
if retry_count == MAX_RETRIES - 1:
|
||||
raise
|
||||
|
||||
time.sleep(delay)
|
||||
delay *= BACKOFF_FACTOR
|
||||
retry_count += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {str(e)}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
test()
|
147
test_client.py
Normal file
147
test_client.py
Normal file
@ -0,0 +1,147 @@
|
||||
import asyncio
|
||||
from typing import Optional
|
||||
from contextlib import AsyncExitStack
|
||||
|
||||
from mcp import ClientSession, StdioServerParameters
|
||||
from mcp.client.stdio import stdio_client
|
||||
|
||||
from anthropic import Anthropic
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv() # load environment variables from .env
|
||||
|
||||
class MCPClient:
|
||||
def __init__(self):
|
||||
# Initialize session and client objects
|
||||
self.session: Optional[ClientSession] = None
|
||||
self.exit_stack = AsyncExitStack()
|
||||
self.anthropic = Anthropic()
|
||||
|
||||
async def connect_to_server(self, server_script_path: str):
|
||||
"""Connect to an MCP server
|
||||
|
||||
Args:
|
||||
server_script_path: Path to the server script (.py or .js)
|
||||
"""
|
||||
is_python = server_script_path.endswith('.py')
|
||||
is_js = server_script_path.endswith('.js')
|
||||
if not (is_python or is_js):
|
||||
raise ValueError("Server script must be a .py or .js file")
|
||||
|
||||
command = "python" if is_python else "node"
|
||||
server_params = StdioServerParameters(
|
||||
command=command,
|
||||
args=[server_script_path],
|
||||
env=None
|
||||
)
|
||||
|
||||
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
|
||||
self.stdio, self.write = stdio_transport
|
||||
self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
|
||||
|
||||
await self.session.initialize()
|
||||
|
||||
# List available tools
|
||||
response = await self.session.list_tools()
|
||||
tools = response.tools
|
||||
print("\nConnected to server with tools:", [tool.name for tool in tools])
|
||||
|
||||
async def process_query(self, query: str) -> str:
|
||||
"""Process a query using Claude and available tools"""
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": query
|
||||
}
|
||||
]
|
||||
|
||||
response = await self.session.list_tools()
|
||||
available_tools = [{
|
||||
"name": tool.name,
|
||||
"description": tool.description,
|
||||
"input_schema": tool.inputSchema
|
||||
} for tool in response.tools]
|
||||
|
||||
# Initial Claude API call
|
||||
response = self.anthropic.messages.create(
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
max_tokens=1000,
|
||||
messages=messages,
|
||||
tools=available_tools
|
||||
)
|
||||
|
||||
# Process response and handle tool calls
|
||||
tool_results = []
|
||||
final_text = []
|
||||
|
||||
for content in response.content:
|
||||
if content.type == 'text':
|
||||
final_text.append(content.text)
|
||||
elif content.type == 'tool_use':
|
||||
tool_name = content.name
|
||||
tool_args = content.input
|
||||
|
||||
# Execute tool call
|
||||
result = await self.session.call_tool(tool_name, tool_args)
|
||||
tool_results.append({"call": tool_name, "result": result})
|
||||
final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
|
||||
|
||||
# Continue conversation with tool results
|
||||
if hasattr(content, 'text') and content.text:
|
||||
messages.append({
|
||||
"role": "assistant",
|
||||
"content": content.text
|
||||
})
|
||||
messages.append({
|
||||
"role": "user",
|
||||
"content": result.content
|
||||
})
|
||||
|
||||
# Get next response from Claude
|
||||
response = self.anthropic.messages.create(
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
max_tokens=1000,
|
||||
messages=messages,
|
||||
)
|
||||
|
||||
final_text.append(response.content[0].text)
|
||||
|
||||
return "\n".join(final_text)
|
||||
|
||||
async def chat_loop(self):
|
||||
"""Run an interactive chat loop"""
|
||||
print("\nMCP Client Started!")
|
||||
print("Type your queries or 'quit' to exit.")
|
||||
|
||||
while True:
|
||||
try:
|
||||
query = input("\nQuery: ").strip()
|
||||
|
||||
if query.lower() == 'quit':
|
||||
break
|
||||
|
||||
response = await self.process_query(query)
|
||||
print("\n" + response)
|
||||
|
||||
except Exception as e:
|
||||
print(f"\nError: {str(e)}")
|
||||
|
||||
async def cleanup(self):
|
||||
"""Clean up resources"""
|
||||
await self.exit_stack.aclose()
|
||||
|
||||
async def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python client.py <path_to_server_script>")
|
||||
sys.exit(1)
|
||||
|
||||
client = MCPClient()
|
||||
try:
|
||||
await client.connect_to_server(sys.argv[1])
|
||||
await client.chat_loop()
|
||||
finally:
|
||||
await client.cleanup()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
asyncio.run(main())
|
31
test_mcp.py
Normal file
31
test_mcp.py
Normal file
@ -0,0 +1,31 @@
|
||||
# import anthropic
|
||||
# import configparser as ini_parser
|
||||
|
||||
# ini = ini_parser.ConfigParser()
|
||||
# ini.read('anthropic.ini')
|
||||
|
||||
# client = anthropic.Anthropic(
|
||||
# api_key = ini['API']['API_KEY']
|
||||
# )
|
||||
|
||||
path_project = "C:\projects\gencpp\base"
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("Demo")
|
||||
|
||||
@mcp.resource("echo://{message}")
|
||||
def echo_resource(message: str) -> str:
|
||||
"""Echo a message"""
|
||||
return f"Resource echo: {message}!"
|
||||
|
||||
@mcp.tool()
|
||||
def echo_tool(message: str) -> str:
|
||||
"""Echo a message as a tool"""
|
||||
return f"Tool echo: {message}"
|
||||
|
||||
|
||||
@mcp.prompt()
|
||||
def echo_prompt(message: str) -> str:
|
||||
"""Create an echo prompt"""
|
||||
return f"Please process this message: {message}"
|
64
test_task.toml
Normal file
64
test_task.toml
Normal file
@ -0,0 +1,64 @@
|
||||
|
||||
[System]
|
||||
content="""
|
||||
Role: Systems engineer aiding with advising modifications to a codebase.
|
||||
Codebase Properties:
|
||||
- C++
|
||||
- staged metaprogramming
|
||||
- library
|
||||
- handmade community philsophy
|
||||
- minimal dependencies
|
||||
- written from scratch
|
||||
- do not use std
|
||||
- do not use templates
|
||||
- Stick to codebase's convention.
|
||||
|
||||
Response format:
|
||||
- Output to file specifier by user prompt.
|
||||
- Each entry is a "Modification Slice".
|
||||
|
||||
Modification Slice, a record made up of the following:
|
||||
<Label>
|
||||
<Filename> <Start> <End>:
|
||||
```cpp
|
||||
<Excerpt-Delta>
|
||||
```
|
||||
<Reasoning>
|
||||
----
|
||||
|
||||
Template arguments for an entry (Modification Slice):
|
||||
- <Filename>: name of the source file
|
||||
- <Start>: Start [Line:Column] of the string excerpt slice within the file.
|
||||
- <End>: End [Line:Column] of the string excerpt slice within the file.
|
||||
- <Exceprt-Delta>: The modification to the excerpt. If its a complete omission just leave blank. Otherwise just show the changed excerpt.
|
||||
- <Reasoning>: Explain the reason for this change.
|
||||
|
||||
The user format will be the following:
|
||||
- background for request
|
||||
- request
|
||||
|
||||
files_path: "C:\\project\\gencpp\\base\\components\\"
|
||||
- types.hpp
|
||||
- parse_types.hpp
|
||||
- code_types.hpp
|
||||
- ast.hpp
|
||||
- interface.hpp
|
||||
- interface.parsing.cpp
|
||||
- lexer.cpp
|
||||
- parser.cpp
|
||||
files_path: "C:\\project\\gencpp\\base\\dependencies\\"
|
||||
- memory.hpp
|
||||
file_response: "C:\\project\\gencpp\\ai\\test_response.md"
|
||||
"""
|
||||
|
||||
[User]
|
||||
background = """
|
||||
- I'm currently in the process of adjusting the parsing interface to support graceful failures. Right now if the parser encounters any notable error when traversing the tokens it will crash the program.
|
||||
- In interface.hpp, structures I'm fleshing out for parsing are located there such as ParseInfo, ParseOpts, & ParseStackNode.
|
||||
- parse_types.hpp has the ParseMessage definition which behaves as a linked list.
|
||||
- ParseOpts currently just contains the allocator overrides for the lifetime of each of the relevant entity categories when parsing.
|
||||
- wip_parse_str is a prototype for a general parse procedure and the other procedures will eventually be refactored to have similar signatures.
|
||||
"""
|
||||
request="""
|
||||
What should be next set of changes based on the state of the parsing interface?
|
||||
"""
|
Reference in New Issue
Block a user