tarantula11 commited on
Commit
fe5151f
·
verified ·
1 Parent(s): 2182976

Upload submission from kinitro-agent-template

Browse files
Files changed (9) hide show
  1. .gitignore +216 -0
  2. .python-version +1 -0
  3. README.md +5 -0
  4. agent.capnp +13 -0
  5. agent.py +165 -0
  6. agent_interface.py +53 -0
  7. agent_server.py +114 -0
  8. main.py +66 -0
  9. pyproject.toml +13 -0
.gitignore ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+ #poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ #pdm.lock
116
+ #pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ #pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ #.idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+
204
+ # Ruff stuff:
205
+ .ruff_cache/
206
+
207
+ # PyPI configuration file
208
+ .pypirc
209
+
210
+ # Marimo
211
+ marimo/_static/
212
+ marimo/_lsp/
213
+ __marimo__/
214
+
215
+ # Streamlit
216
+ .streamlit/secrets.toml
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.12
README.md ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # Kinitro Agent Template
2
+
3
+ A template to help you get started with submitting agents to Kinitro.
4
+
5
+ The main Kinitro repository can be found [here](https://github.com/threetau/kinitro).
agent.capnp ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @0x893bac407c81b48c;
2
+
3
+ interface Agent {
4
+
5
+ struct Tensor {
6
+ data @0 :Data; # tensor bytes tensor.numpy().tobytes()
7
+ shape @1 :List(UInt64); # tensor shape list(tensor.shape())
8
+ dtype @2 :Text; # data type name tensor.dtype()
9
+ }
10
+
11
+ act @0 (obs :Data) -> (action :Tensor);
12
+ reset @1 () -> ();
13
+ }
agent.py ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Implementation of the AgentInterface for MetaWorld tasks.
3
+
4
+ This agent uses the SawyerPickPlaceV2Policy from MetaWorld as an expert policy.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, Dict
9
+
10
+ import gymnasium as gym
11
+ import metaworld
12
+ import numpy as np
13
+ import torch
14
+ from agent_interface import AgentInterface
15
+ from metaworld.policies.sawyer_reach_v3_policy import SawyerReachV3Policy
16
+
17
+
18
+ class RLAgent(AgentInterface):
19
+ """
20
+ MetaWorld agent implementation using the SawyerReachV3Policy expert policy.
21
+
22
+ This agent uses the expert policy from MetaWorld for reach tasks.
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ observation_space: gym.Space | None = None,
28
+ action_space: gym.Space | None = None,
29
+ seed: int | None = None,
30
+ **kwargs,
31
+ ):
32
+ super().__init__(observation_space, action_space, seed, **kwargs)
33
+
34
+ self.logger = logging.getLogger(__name__)
35
+ self.logger.info(f"Initializing MetaWorld agent with seed {self.seed}")
36
+
37
+ self.policy = SawyerReachV3Policy()
38
+ self.logger.info("Successfully initialized SawyerReachV3Policy")
39
+
40
+ # Track episode state
41
+ self.episode_step = 0
42
+ self.max_episode_steps = kwargs.get("max_episode_steps", 200)
43
+
44
+ self.logger.info("MetaWorld agent initialized successfully")
45
+
46
+ def act(self, obs: Dict[str, Any], **kwargs) -> torch.Tensor:
47
+ """
48
+ Process the observation and return an action using the MetaWorld expert policy.
49
+
50
+ Args:
51
+ obs: Observation from the environment
52
+ kwargs: Additional arguments
53
+
54
+ Returns:
55
+ action: Action tensor to take in the environment
56
+ """
57
+ try:
58
+ # Process observation to extract the format needed by the expert policy
59
+ processed_obs = self._process_observation(obs)
60
+
61
+ # Use the expert policy (MetaWorld is always available)
62
+ # MetaWorld policies expect numpy arrays
63
+ action_numpy = self.policy.get_action(processed_obs)
64
+ action_tensor = torch.from_numpy(np.array(action_numpy)).float()
65
+
66
+ # Log occasionally
67
+ if self.episode_step % 50 == 0:
68
+ self.logger.debug(f"Using expert policy action: {action_numpy}")
69
+
70
+ # Increment episode step
71
+ self.episode_step += 1
72
+
73
+ # Occasionally log actions to avoid spam
74
+ if self.episode_step % 50 == 0:
75
+ self.logger.debug(
76
+ f"Step {self.episode_step}: Action shape {action_tensor.shape}"
77
+ )
78
+
79
+ return action_tensor
80
+
81
+ except Exception as e:
82
+ self.logger.error(f"Error in act method: {e}", exc_info=True)
83
+ # Return zeros as a fallback
84
+ if isinstance(self.action_space, gym.spaces.Box):
85
+ return torch.zeros(self.action_space.shape[0], dtype=torch.float32)
86
+ else:
87
+ return torch.zeros(4, dtype=torch.float32)
88
+
89
+ def _process_observation(self, obs):
90
+ """
91
+ Helper method to process observations for the MetaWorld expert policy.
92
+
93
+ MetaWorld policies typically expect a specific observation format.
94
+ """
95
+ if isinstance(obs, dict):
96
+ # MetaWorld environment can return observations in different formats
97
+ if "observation" in obs:
98
+ # Standard format for goal-observable environments
99
+ processed_obs = obs["observation"]
100
+ elif "obs" in obs:
101
+ processed_obs = obs["obs"]
102
+ elif "state_observation" in obs:
103
+ # Some MetaWorld environments use this key
104
+ processed_obs = obs["state_observation"]
105
+ elif "goal_achieved" in obs:
106
+ # If we have information about goal achievement
107
+ # This might be needed for certain policy decisions
108
+ achievement = obs.get("goal_achieved", False)
109
+ base_obs = next(iter(obs.values()))
110
+ self.logger.debug(f"Goal achieved: {achievement}")
111
+ processed_obs = base_obs
112
+ else:
113
+ # If structure is unknown, use the first value
114
+ processed_obs = next(iter(obs.values()))
115
+ self.logger.debug(f"Using observation key: {next(iter(obs.keys()))}")
116
+ else:
117
+ # If already a numpy array or similar, use directly
118
+ processed_obs = obs
119
+
120
+ # Ensure we're returning a numpy array as expected by MetaWorld policies
121
+ if not isinstance(processed_obs, np.ndarray):
122
+ try:
123
+ processed_obs = np.array(processed_obs, dtype=np.float32)
124
+ except Exception as e:
125
+ self.logger.error(f"Failed to convert observation to numpy array: {e}")
126
+ # Return a dummy observation if conversion fails
127
+ if (
128
+ self.observation_space
129
+ and hasattr(self.observation_space, "shape")
130
+ and self.observation_space.shape is not None
131
+ ):
132
+ processed_obs = np.zeros(
133
+ self.observation_space.shape, dtype=np.float32
134
+ )
135
+ else:
136
+ # Typical MetaWorld observation dimension if all else fails
137
+ processed_obs = np.zeros(39, dtype=np.float32)
138
+
139
+ return processed_obs
140
+
141
+ def reset(self) -> None:
142
+ """
143
+ Reset agent state between episodes.
144
+ """
145
+ self.logger.debug("Resetting agent")
146
+ self.episode_step = 0
147
+ # Any other stateful components would be reset here
148
+
149
+ def _build_model(self):
150
+ """
151
+ Build a neural network model for the agent.
152
+
153
+ This is a placeholder for where you would define your neural network
154
+ architecture using PyTorch, TensorFlow, or another framework.
155
+ """
156
+ # Example of where you might build a simple PyTorch model
157
+ # model = torch.nn.Sequential(
158
+ # torch.nn.Linear(self.observation_space.shape[0], 128),
159
+ # torch.nn.ReLU(),
160
+ # torch.nn.Linear(128, 64),
161
+ # torch.nn.ReLU(),
162
+ # torch.nn.Linear(64, self.action_space.shape[0]),
163
+ # )
164
+ # return model
165
+ pass
agent_interface.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Abstract base class defining the standard interface for all agents.
3
+
4
+ All miner-submitted agents must implement this interface to be evaluated.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+
9
+ import gymnasium as gym
10
+ import numpy as np
11
+ import torch
12
+
13
+
14
+ class AgentInterface(ABC):
15
+ """
16
+ Standard interface that all miner implementations must follow.
17
+
18
+ This ensures a consistent contract between the evaluator and any submitted agent,
19
+ regardless of the underlying model architecture or implementation details.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ observation_space: gym.Space | None = None,
25
+ action_space: gym.Space | None = None,
26
+ seed: int | None = None,
27
+ **kwargs,
28
+ ):
29
+ self.observation_space = observation_space or gym.spaces.Box(
30
+ low=-1, high=1, shape=(100,), dtype=np.float32
31
+ )
32
+ self.action_space = action_space or gym.spaces.Box(
33
+ low=-1, high=1, shape=(4,), dtype=np.float32
34
+ )
35
+ self.seed = seed or np.random.randint(0, 1000000)
36
+ self.rng = np.random.default_rng(seed)
37
+
38
+ @abstractmethod
39
+ def act(self, obs: dict, **kwargs) -> torch.Tensor:
40
+ """
41
+ Take action given current observation and any additional arguments.
42
+ """
43
+ pass
44
+
45
+ def reset(self) -> None:
46
+ """
47
+ Reset agent state for new episode.
48
+
49
+ This is called at the beginning of each episode. Stateless agents
50
+ can implement this as a no-op. Agents with internal memory/history
51
+ should reset their state here.
52
+ """
53
+ pass
agent_server.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Cap'n Proto RPC Server for Agent Interface
4
+ """
5
+
6
+ import asyncio
7
+ import logging
8
+ import os
9
+ import pickle
10
+ import numpy as np
11
+ import torch
12
+ import capnp
13
+
14
+ # Load the schema
15
+ schema_file = os.path.join(os.path.dirname(__file__), "agent.capnp")
16
+ agent_capnp = capnp.load(schema_file)
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class AgentServer(agent_capnp.Agent.Server):
22
+ """Cap'n Proto server implementation for AgentInterface"""
23
+
24
+ def __init__(self, agent):
25
+ self.agent = agent
26
+ self.logger = logging.getLogger(__name__)
27
+ self.logger.info("AgentServer initialized with agent: %s", type(agent).__name__)
28
+
29
+ async def act(self, obs, **kwargs):
30
+ """Handle act RPC call"""
31
+ try:
32
+ # Deserialize observation from bytes
33
+ observation = pickle.loads(obs)
34
+
35
+ # Call the agent's act method
36
+ action_tensor = self.agent.act(observation)
37
+
38
+ # Convert to numpy if it's a torch tensor
39
+ if isinstance(action_tensor, torch.Tensor):
40
+ action_numpy = action_tensor.detach().cpu().numpy()
41
+ else:
42
+ action_numpy = np.array(action_tensor)
43
+
44
+ # Prepare tensor response
45
+ response = agent_capnp.Agent.Tensor.new_message()
46
+ response.data = action_numpy.tobytes()
47
+ response.shape = list(action_numpy.shape)
48
+ response.dtype = str(action_numpy.dtype)
49
+
50
+ return response
51
+ except Exception as e:
52
+ self.logger.error(f"Error in act: {e}", exc_info=True)
53
+ raise
54
+
55
+ async def reset(self, **kwargs):
56
+ """Handle reset RPC call"""
57
+ try:
58
+ self.agent.reset()
59
+ except Exception as e:
60
+ self.logger.error(f"Error in reset: {e}", exc_info=True)
61
+ raise
62
+
63
+
64
+ async def serve(agent, address="127.0.0.1", port=8000):
65
+ """Serve the agent using asyncio approach"""
66
+
67
+ async def new_connection(stream):
68
+ """Handler for each new client connection"""
69
+ try:
70
+ # Create TwoPartyServer for this connection
71
+ server = capnp.TwoPartyServer(stream, bootstrap=AgentServer(agent))
72
+
73
+ # Wait for the connection to disconnect
74
+ await server.on_disconnect()
75
+
76
+ except Exception as e:
77
+ logger.error(f"Error handling connection: {e}", exc_info=True)
78
+
79
+ # Create the server
80
+ server = await capnp.AsyncIoStream.create_server(new_connection, address, port)
81
+
82
+ logger.info(f"Agent RPC server listening on {address}:{port}")
83
+
84
+ try:
85
+ # Keep the server running
86
+ async with server:
87
+ await server.serve_forever()
88
+ except Exception as e:
89
+ logger.error(f"Server error: {e}", exc_info=True)
90
+ finally:
91
+ logger.info("Server shutting down")
92
+
93
+
94
+ def start_server(agent, address="127.0.0.1", port=8000):
95
+ """Start server with proper asyncio event loop handling"""
96
+
97
+ async def run_server_with_kj():
98
+ async with capnp.kj_loop():
99
+ await serve(agent, address, port)
100
+
101
+ try:
102
+ asyncio.run(run_server_with_kj())
103
+ except KeyboardInterrupt:
104
+ logger.info("Server stopped by user")
105
+
106
+
107
+ def run_server_in_process(agent, address="127.0.0.1", port=8000):
108
+ """Entry point for running server in a separate process"""
109
+
110
+ async def run_with_kj():
111
+ async with capnp.kj_loop():
112
+ await serve(agent, address, port)
113
+
114
+ asyncio.run(run_with_kj())
main.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Main entry point for the agent server.
4
+
5
+ This script creates an agent implementation and starts the RPC server
6
+ to handle requests from the evaluator.
7
+ """
8
+
9
+ import argparse
10
+ import logging
11
+ import sys
12
+
13
+ from agent import RLAgent
14
+ from agent_server import start_server
15
+
16
+
17
+ def setup_logging(level=logging.INFO):
18
+ """Configure logging."""
19
+ logging.basicConfig(
20
+ level=level,
21
+ format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
22
+ handlers=[logging.StreamHandler(sys.stdout)],
23
+ )
24
+
25
+
26
+ def main():
27
+ """Main entry point."""
28
+ parser = argparse.ArgumentParser(description="Start the agent server")
29
+ parser.add_argument(
30
+ "--host", type=str, default="*", help="Host to bind the server to"
31
+ )
32
+ parser.add_argument(
33
+ "--port", type=int, default=8000, help="Port to bind the server to"
34
+ )
35
+ parser.add_argument(
36
+ "--log-level",
37
+ type=str,
38
+ default="INFO",
39
+ choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
40
+ help="Logging level",
41
+ )
42
+
43
+ args = parser.parse_args()
44
+
45
+ # Setup logging
46
+ log_level = getattr(logging, args.log_level)
47
+ setup_logging(log_level)
48
+ logger = logging.getLogger(__name__)
49
+
50
+ logger.info(f"Starting agent server on {args.host}:{args.port}")
51
+
52
+ # Create the RLAgent
53
+ agent = RLAgent()
54
+
55
+ # Start the server
56
+ try:
57
+ start_server(agent, args.host, args.port)
58
+ except KeyboardInterrupt:
59
+ logger.info("Server stopped by user")
60
+ except Exception as e:
61
+ logger.error(f"Error starting server: {e}", exc_info=True)
62
+ sys.exit(1)
63
+
64
+
65
+ if __name__ == "__main__":
66
+ main()
pyproject.toml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "kinitro-agent"
3
+ version = "0.0.1"
4
+ description = "Kinitro Agent"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "metaworld>=3.0.0",
9
+ "torch>=2.8.0"
10
+ ]
11
+
12
+ [dependency-groups]
13
+ dev = ["debugpy>=1.8.9", "py-spy>=0.4.0", "pytest>=8.3.4", "ruff>=0.8.2"]