pybiolib 1.2.883__py3-none-any.whl → 1.2.1890__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. biolib/__init__.py +33 -10
  2. biolib/_data_record/data_record.py +220 -126
  3. biolib/_index/index.py +55 -0
  4. biolib/_index/query_result.py +103 -0
  5. biolib/_internal/add_copilot_prompts.py +24 -11
  6. biolib/_internal/add_gui_files.py +81 -0
  7. biolib/_internal/data_record/__init__.py +1 -1
  8. biolib/_internal/data_record/data_record.py +1 -18
  9. biolib/_internal/data_record/push_data.py +65 -16
  10. biolib/_internal/data_record/remote_storage_endpoint.py +18 -13
  11. biolib/_internal/file_utils.py +48 -0
  12. biolib/_internal/lfs/cache.py +4 -2
  13. biolib/_internal/push_application.py +95 -24
  14. biolib/_internal/runtime.py +2 -0
  15. biolib/_internal/string_utils.py +13 -0
  16. biolib/_internal/{llm_instructions → templates/copilot_template}/.github/instructions/style-general.instructions.md +5 -0
  17. biolib/_internal/templates/copilot_template/.github/instructions/style-react-ts.instructions.md +47 -0
  18. biolib/_internal/templates/copilot_template/.github/prompts/biolib_onboard_repo.prompt.md +19 -0
  19. biolib/_internal/templates/dashboard_template/.biolib/config.yml +5 -0
  20. biolib/_internal/templates/{init_template → github_workflow_template}/.github/workflows/biolib.yml +7 -2
  21. biolib/_internal/templates/gitignore_template/.gitignore +10 -0
  22. biolib/_internal/templates/gui_template/.yarnrc.yml +1 -0
  23. biolib/_internal/templates/gui_template/App.tsx +53 -0
  24. biolib/_internal/templates/gui_template/Dockerfile +27 -0
  25. biolib/_internal/templates/gui_template/biolib-sdk.ts +82 -0
  26. biolib/_internal/templates/gui_template/dev-data/output.json +7 -0
  27. biolib/_internal/templates/gui_template/index.css +5 -0
  28. biolib/_internal/templates/gui_template/index.html +13 -0
  29. biolib/_internal/templates/gui_template/index.tsx +10 -0
  30. biolib/_internal/templates/gui_template/package.json +27 -0
  31. biolib/_internal/templates/gui_template/tsconfig.json +24 -0
  32. biolib/_internal/templates/gui_template/vite-plugin-dev-data.ts +50 -0
  33. biolib/_internal/templates/gui_template/vite.config.mts +10 -0
  34. biolib/_internal/templates/init_template/.biolib/config.yml +1 -0
  35. biolib/_internal/templates/init_template/Dockerfile +5 -1
  36. biolib/_internal/templates/init_template/run.py +6 -15
  37. biolib/_internal/templates/init_template/run.sh +1 -0
  38. biolib/_internal/templates/templates.py +21 -1
  39. biolib/_internal/utils/__init__.py +47 -0
  40. biolib/_internal/utils/auth.py +46 -0
  41. biolib/_internal/utils/job_url.py +33 -0
  42. biolib/_internal/utils/multinode.py +12 -14
  43. biolib/_runtime/runtime.py +15 -2
  44. biolib/_session/session.py +7 -5
  45. biolib/_shared/__init__.py +0 -0
  46. biolib/_shared/types/__init__.py +74 -0
  47. biolib/_shared/types/account.py +12 -0
  48. biolib/_shared/types/account_member.py +8 -0
  49. biolib/{_internal → _shared}/types/experiment.py +1 -0
  50. biolib/_shared/types/resource.py +37 -0
  51. biolib/_shared/types/resource_deploy_key.py +11 -0
  52. biolib/{_internal → _shared}/types/resource_version.py +8 -2
  53. biolib/_shared/types/user.py +19 -0
  54. biolib/_shared/utils/__init__.py +7 -0
  55. biolib/_shared/utils/resource_uri.py +75 -0
  56. biolib/api/client.py +5 -48
  57. biolib/app/app.py +97 -55
  58. biolib/biolib_api_client/api_client.py +3 -47
  59. biolib/biolib_api_client/app_types.py +1 -1
  60. biolib/biolib_api_client/biolib_app_api.py +31 -6
  61. biolib/biolib_api_client/biolib_job_api.py +1 -1
  62. biolib/biolib_api_client/user_state.py +34 -2
  63. biolib/biolib_binary_format/module_input.py +8 -0
  64. biolib/biolib_binary_format/remote_endpoints.py +3 -3
  65. biolib/biolib_binary_format/remote_stream_seeker.py +39 -25
  66. biolib/biolib_logging.py +1 -1
  67. biolib/cli/__init__.py +2 -2
  68. biolib/cli/auth.py +4 -16
  69. biolib/cli/data_record.py +82 -0
  70. biolib/cli/index.py +32 -0
  71. biolib/cli/init.py +393 -71
  72. biolib/cli/lfs.py +1 -1
  73. biolib/cli/run.py +9 -6
  74. biolib/cli/start.py +14 -1
  75. biolib/compute_node/job_worker/executors/docker_executor.py +31 -9
  76. biolib/compute_node/job_worker/executors/docker_types.py +1 -1
  77. biolib/compute_node/job_worker/executors/types.py +6 -5
  78. biolib/compute_node/job_worker/job_storage.py +2 -1
  79. biolib/compute_node/job_worker/job_worker.py +155 -90
  80. biolib/compute_node/job_worker/large_file_system.py +2 -6
  81. biolib/compute_node/job_worker/network_alloc.py +99 -0
  82. biolib/compute_node/job_worker/network_buffer.py +240 -0
  83. biolib/compute_node/job_worker/utilization_reporter_thread.py +2 -2
  84. biolib/compute_node/remote_host_proxy.py +163 -79
  85. biolib/compute_node/utils.py +2 -0
  86. biolib/compute_node/webserver/compute_node_results_proxy.py +189 -0
  87. biolib/compute_node/webserver/proxy_utils.py +28 -0
  88. biolib/compute_node/webserver/webserver.py +64 -19
  89. biolib/experiments/experiment.py +111 -16
  90. biolib/jobs/job.py +128 -31
  91. biolib/jobs/job_result.py +74 -34
  92. biolib/jobs/types.py +1 -0
  93. biolib/sdk/__init__.py +28 -3
  94. biolib/typing_utils.py +1 -1
  95. biolib/utils/cache_state.py +8 -5
  96. biolib/utils/multipart_uploader.py +24 -18
  97. biolib/utils/seq_util.py +1 -1
  98. pybiolib-1.2.1890.dist-info/METADATA +41 -0
  99. pybiolib-1.2.1890.dist-info/RECORD +177 -0
  100. {pybiolib-1.2.883.dist-info → pybiolib-1.2.1890.dist-info}/WHEEL +1 -1
  101. pybiolib-1.2.1890.dist-info/entry_points.txt +2 -0
  102. biolib/_internal/llm_instructions/.github/instructions/style-react-ts.instructions.md +0 -22
  103. biolib/_internal/templates/init_template/.gitignore +0 -2
  104. biolib/_internal/types/__init__.py +0 -6
  105. biolib/_internal/types/resource.py +0 -18
  106. biolib/biolib_download_container.py +0 -38
  107. biolib/cli/download_container.py +0 -14
  108. biolib/utils/app_uri.py +0 -57
  109. pybiolib-1.2.883.dist-info/METADATA +0 -50
  110. pybiolib-1.2.883.dist-info/RECORD +0 -148
  111. pybiolib-1.2.883.dist-info/entry_points.txt +0 -3
  112. /biolib/{_internal/llm_instructions → _index}/__init__.py +0 -0
  113. /biolib/_internal/{llm_instructions → templates/copilot_template}/.github/instructions/general-app-knowledge.instructions.md +0 -0
  114. /biolib/_internal/{llm_instructions → templates/copilot_template}/.github/instructions/style-python.instructions.md +0 -0
  115. /biolib/_internal/{llm_instructions → templates/copilot_template}/.github/prompts/biolib_app_inputs.prompt.md +0 -0
  116. /biolib/_internal/{llm_instructions → templates/copilot_template}/.github/prompts/biolib_run_apps.prompt.md +0 -0
  117. /biolib/{_internal → _shared}/types/app.py +0 -0
  118. /biolib/{_internal → _shared}/types/data_record.py +0 -0
  119. /biolib/{_internal → _shared}/types/file_node.py +0 -0
  120. /biolib/{_internal → _shared}/types/push.py +0 -0
  121. /biolib/{_internal → _shared}/types/resource_permission.py +0 -0
  122. /biolib/{_internal → _shared}/types/result.py +0 -0
  123. /biolib/{_internal → _shared}/types/typing.py +0 -0
  124. {pybiolib-1.2.883.dist-info → pybiolib-1.2.1890.dist-info/licenses}/LICENSE +0 -0
biolib/cli/run.py CHANGED
@@ -13,19 +13,23 @@ from biolib.typing_utils import Optional, Tuple
13
13
  help='Run an application on BioLib.',
14
14
  )
15
15
  @click.option('--experiment', type=str, required=False, help='Experiment name or URI to add the run to.')
16
- @click.option('--local', is_flag=True, required=False, help='Run the application locally.')
16
+ @click.option('--local', is_flag=True, required=False, hidden=True)
17
17
  @click.option('--non-blocking', is_flag=True, required=False, help='Run the application non blocking.')
18
18
  @click.argument('uri', required=True)
19
19
  @click.argument('args', nargs=-1, type=click.UNPROCESSED)
20
20
  def run(experiment: Optional[str], local: bool, non_blocking: bool, uri: str, args: Tuple[str]) -> None:
21
+ if local:
22
+ print('Error: Running applications locally with --local is no longer supported.', file=sys.stderr)
23
+ sys.exit(1)
24
+
21
25
  if experiment:
22
26
  with Experiment(uri=experiment):
23
- _run(local=local, non_blocking=non_blocking, uri=uri, args=args)
27
+ _run(non_blocking=non_blocking, uri=uri, args=args)
24
28
  else:
25
- _run(local=local, non_blocking=non_blocking, uri=uri, args=args)
29
+ _run(non_blocking=non_blocking, uri=uri, args=args)
26
30
 
27
31
 
28
- def _run(local: bool, non_blocking: bool, uri: str, args: Tuple[str]) -> None:
32
+ def _run(non_blocking: bool, uri: str, args: Tuple[str]) -> None:
29
33
  try:
30
34
  app = BioLibApp(uri=uri)
31
35
  except biolib_errors.BioLibError as error:
@@ -43,12 +47,11 @@ def _run(local: bool, non_blocking: bool, uri: str, args: Tuple[str]) -> None:
43
47
  args=list(args),
44
48
  stdin=_get_stdin(),
45
49
  files=None,
46
- machine=('local' if local else ''),
47
50
  blocking=blocking,
48
51
  )
49
52
 
50
53
  if blocking:
51
- job.save_files('biolib_results')
54
+ job.save_files('biolib_results', overwrite=True)
52
55
 
53
56
  # Write stdout and stderr if it has not been streamed (Markdown is not streamed)
54
57
  if app.version.get('stdout_render_type') == 'markdown' or not sys.stdout.isatty():
biolib/cli/start.py CHANGED
@@ -13,7 +13,10 @@ from biolib.typing_utils import Optional
13
13
  @click.option('--port', default=5000, type=click.IntRange(1, 65_535), required=False)
14
14
  @click.option('--tls-certificate', type=click.Path(exists=True), required=False, hidden=True)
15
15
  @click.option('--tls-key', type=click.Path(exists=True), required=False, hidden=True)
16
- def start(host: str, port: int, tls_certificate: Optional[str], tls_key: Optional[str]) -> None:
16
+ @click.option('--initialize-network-buffer', is_flag=True, help='Initialize the remote host network buffer and exit')
17
+ def start(
18
+ host: str, port: int, tls_certificate: Optional[str], tls_key: Optional[str], initialize_network_buffer: bool
19
+ ) -> None:
17
20
  logger.configure(default_log_level=logging.INFO)
18
21
  logger_no_user_data.configure(default_log_level=logging.INFO)
19
22
  if platform.system() == 'Windows':
@@ -22,6 +25,16 @@ def start(host: str, port: int, tls_certificate: Optional[str], tls_key: Optiona
22
25
  if tls_certificate and not tls_key or tls_key and not tls_certificate:
23
26
  raise Exception('Options --tls-certificate and --tls-key must be specified together')
24
27
 
28
+ if initialize_network_buffer:
29
+ from biolib.compute_node.job_worker.network_buffer import ( # pylint: disable=import-outside-toplevel
30
+ NetworkBuffer,
31
+ )
32
+
33
+ network_buffer = NetworkBuffer.get_instance()
34
+ created = network_buffer.fill_buffer()
35
+ logger_no_user_data.info(f'Initialized network buffer (created {created} networks)')
36
+ return
37
+
25
38
  try:
26
39
  from biolib.compute_node.webserver import webserver # pylint: disable=import-outside-toplevel
27
40
 
@@ -11,10 +11,10 @@ import zipfile
11
11
  from copy import copy
12
12
  from datetime import datetime
13
13
 
14
- import docker # type: ignore
15
- import docker.types # type: ignore
16
- from docker.errors import APIError, ImageNotFound # type: ignore
17
- from docker.models.containers import Container # type: ignore
14
+ import docker
15
+ import docker.types
16
+ from docker.errors import APIError, ImageNotFound
17
+ from docker.models.containers import Container
18
18
 
19
19
  from biolib import utils
20
20
  from biolib._internal.runtime import RuntimeJobDataDict
@@ -40,7 +40,7 @@ class DockerExecutor:
40
40
  self._options: LocalExecutorOptions = options
41
41
  self._is_cleaning_up = False
42
42
 
43
- self._absolute_image_uri = f"{utils.BIOLIB_SITE_HOSTNAME}/{self._options['module']['image_uri']}"
43
+ self._absolute_image_uri = f'{utils.BIOLIB_SITE_HOSTNAME}/{self._options["module"]["image_uri"]}'
44
44
  self._send_system_exception = options['send_system_exception']
45
45
  self._send_stdout_and_stderr = options['send_stdout_and_stderr']
46
46
  self._random_docker_id = compute_node_utils.random_string(15)
@@ -308,7 +308,8 @@ class DockerExecutor:
308
308
  job_uuid = self._options['job']['public_id']
309
309
  logger_no_user_data.debug(f'Job "{job_uuid}" initializing Docker container...')
310
310
  module = self._options['module']
311
- logger.debug(f"Initializing docker container with command: {module['command']}")
311
+ logger.debug(f'Initializing docker container with command: {module["command"]}')
312
+ docker_client = BiolibDockerClient.get_docker_client()
312
313
 
313
314
  docker_volume_mounts = [lfs.docker_mount for lfs in self._options['large_file_systems'].values()]
314
315
 
@@ -318,10 +319,12 @@ class DockerExecutor:
318
319
  biolib_system_secret = RuntimeJobDataDict(
319
320
  version='1.0.0',
320
321
  job_requested_machine=self._options['job']['requested_machine'],
322
+ job_requested_machine_spot=self._options['job'].get('requested_machine_spot', False),
321
323
  job_uuid=self._options['job']['public_id'],
322
324
  job_auth_token=self._options['job']['auth_token'],
323
325
  app_uri=self._options['job']['app_uri'],
324
326
  is_environment_biolib_cloud=bool(utils.IS_RUNNING_IN_CLOUD),
327
+ job_reserved_machines=self._options['job']['reserved_machines'],
325
328
  )
326
329
  docker_volume_mounts.append(
327
330
  self._create_secrets_mount(
@@ -365,9 +368,11 @@ class DockerExecutor:
365
368
  )
366
369
 
367
370
  logger_no_user_data.debug(f'Job "{job_uuid}" initializing Docker container. Getting IPs for proxies...')
371
+
372
+ networks_to_connect = []
368
373
  for proxy in self._options['remote_host_proxies']:
369
- proxy_ip = proxy.get_ip_address_on_network(internal_network)
370
374
  if proxy.is_app_caller_proxy:
375
+ proxy_ip = proxy.get_ip_address_on_network(internal_network)
371
376
  logger_no_user_data.debug('Found app caller proxy, setting both base URLs in compute container')
372
377
  environment_vars.update(
373
378
  {
@@ -381,7 +386,11 @@ class DockerExecutor:
381
386
  }
382
387
  )
383
388
  else:
384
- extra_hosts[proxy.hostname] = proxy_ip
389
+ extra_hosts.update(proxy.get_hostname_to_ip_mapping())
390
+
391
+ for network in proxy.get_remote_host_networks():
392
+ if network != internal_network:
393
+ networks_to_connect.append(network)
385
394
 
386
395
  logger_no_user_data.debug(f'Job "{job_uuid}" initializing Docker container. Constructing container args...')
387
396
  create_container_args = {
@@ -391,6 +400,9 @@ class DockerExecutor:
391
400
  'mounts': docker_volume_mounts,
392
401
  'network': internal_network.name,
393
402
  'working_dir': module['working_directory'],
403
+ 'networking_config': {
404
+ internal_network.name: docker_client.api.create_endpoint_config(aliases=['main'])
405
+ },
394
406
  }
395
407
 
396
408
  if self._options['job'].get('arguments_override_command'):
@@ -429,9 +441,19 @@ class DockerExecutor:
429
441
  if docker_runtime is not None:
430
442
  create_container_args['runtime'] = docker_runtime
431
443
 
432
- docker_client = BiolibDockerClient.get_docker_client()
433
444
  logger_no_user_data.debug(f'Job "{job_uuid}" initializing Docker container. Creating container...')
434
445
  self._docker_container = docker_client.containers.create(**create_container_args)
446
+
447
+ if networks_to_connect:
448
+ network_connection_start = time.time()
449
+ for network in networks_to_connect:
450
+ network.connect(self._docker_container.id)
451
+ logger_no_user_data.debug(f'Connected app container to network {network.name}')
452
+ network_connection_time = time.time() - network_connection_start
453
+ logger_no_user_data.debug(
454
+ f'Connected app container to {len(networks_to_connect)} networks in {network_connection_time:.2f}s'
455
+ )
456
+
435
457
  logger_no_user_data.debug(f'Job "{job_uuid}" finished initializing Docker container.')
436
458
  except Exception as exception:
437
459
  raise ComputeProcessException(
@@ -1,6 +1,6 @@
1
1
  import enum
2
2
 
3
- from biolib.typing_utils import TypedDict, Any
3
+ from biolib.typing_utils import Any, TypedDict
4
4
 
5
5
 
6
6
  class Proxy(TypedDict):
@@ -1,11 +1,11 @@
1
- from docker.models.networks import Network # type: ignore
1
+ from docker.models.networks import Network
2
2
 
3
+ from biolib.biolib_api_client.app_types import Module
4
+ from biolib.biolib_api_client.job_types import CloudJob, CreatedJobDict
3
5
  from biolib.compute_node.job_worker.large_file_system import LargeFileSystem
4
- from biolib.compute_node.webserver.webserver_types import ComputeNodeInfo
5
- from biolib.typing_utils import TypedDict, Callable, Optional, List, Dict
6
6
  from biolib.compute_node.remote_host_proxy import RemoteHostProxy
7
- from biolib.biolib_api_client.app_types import Module
8
- from biolib.biolib_api_client.job_types import CreatedJobDict, CloudJob
7
+ from biolib.compute_node.webserver.webserver_types import ComputeNodeInfo
8
+ from biolib.typing_utils import Callable, Dict, List, Optional, TypedDict
9
9
 
10
10
 
11
11
  class StatusUpdate(TypedDict):
@@ -43,6 +43,7 @@ class LocalExecutorOptions(TypedDict):
43
43
  send_system_exception: SendSystemExceptionType
44
44
  send_stdout_and_stderr: SendStdoutAndStderrType
45
45
 
46
+
46
47
  class MetadataToSaveOutput(TypedDict):
47
48
  arguments: List[str]
48
49
  startup_error_string: Optional[str]
@@ -97,11 +97,12 @@ class JobStorage:
97
97
  @staticmethod
98
98
  def download_module_input(job: CreatedJobDict, path: str):
99
99
  job_uuid = job['public_id']
100
- logger_no_user_data.debug(f'Job "{job_uuid}" downloading module input...')
100
+ logger_no_user_data.debug(f'Job "{job_uuid}" getting module input url...')
101
101
  presigned_download_url = BiolibJobApi.get_job_storage_download_url(
102
102
  job_uuid=job_uuid,
103
103
  job_auth_token=job['auth_token'],
104
104
  storage_type='input',
105
105
  )
106
+ logger_no_user_data.debug(f'Job "{job_uuid}" downloading module input...')
106
107
  HttpClient.request(url=presigned_download_url, response_path=path)
107
108
  logger_no_user_data.debug(f'Job "{job_uuid}" module input downloaded')