simula 0.1.0__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.
- simula/__init__.py +15 -0
- simula/__main__.py +42 -0
- simula/backends.py +109 -0
- simula/loop.py +74 -0
- simula/py.typed +0 -0
- simula/workspace.py +35 -0
- simula-0.1.0.dist-info/METADATA +79 -0
- simula-0.1.0.dist-info/RECORD +12 -0
- simula-0.1.0.dist-info/WHEEL +5 -0
- simula-0.1.0.dist-info/entry_points.txt +2 -0
- simula-0.1.0.dist-info/licenses/LICENSE +21 -0
- simula-0.1.0.dist-info/top_level.txt +1 -0
simula/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""simula — a local-first engine for generating and inhabiting worlds and personas from the
|
|
2
|
+
user's own materials. See PLAN.md and PRINCIPLES.md.
|
|
3
|
+
|
|
4
|
+
One engine, two blueprint types (world | persona), one unified entity model (Simulacrum).
|
|
5
|
+
"""
|
|
6
|
+
from .backends import Backend, Contract, Message, from_config
|
|
7
|
+
from .loop import Simulacrum, TurnResult, run_turn
|
|
8
|
+
from .workspace import bootstrap_workspace, default_workspace
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Backend", "Contract", "Message", "from_config",
|
|
12
|
+
"Simulacrum", "TurnResult", "run_turn",
|
|
13
|
+
"bootstrap_workspace", "default_workspace",
|
|
14
|
+
]
|
|
15
|
+
__version__ = "0.1.0"
|
simula/__main__.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Minimal CLI entry point for `simula`.
|
|
2
|
+
|
|
3
|
+
Currently (Phase 0) it only exposes `version`, `init` (workspace bootstrap), and `where`. The
|
|
4
|
+
engine is still a skeleton; see PLAN.md for the implementation phases.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
|
|
10
|
+
from . import __version__
|
|
11
|
+
from .workspace import bootstrap_workspace, default_workspace
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main(argv: list[str] | None = None) -> int:
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
prog="simula",
|
|
17
|
+
description="A local-first engine for generating and inhabiting worlds and personas.",
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument("--version", action="version", version=f"simula {__version__}")
|
|
20
|
+
sub = parser.add_subparsers(dest="command")
|
|
21
|
+
|
|
22
|
+
p_init = sub.add_parser("init", help="Create the workspace folder tree.")
|
|
23
|
+
p_init.add_argument("path", nargs="?", default=None, help="Path (default: platform default).")
|
|
24
|
+
|
|
25
|
+
sub.add_parser("where", help="Print the default workspace path.")
|
|
26
|
+
|
|
27
|
+
args = parser.parse_args(argv)
|
|
28
|
+
|
|
29
|
+
if args.command == "init":
|
|
30
|
+
ws = bootstrap_workspace(args.path)
|
|
31
|
+
print(f"Workspace ready: {ws}")
|
|
32
|
+
return 0
|
|
33
|
+
if args.command == "where":
|
|
34
|
+
print(default_workspace())
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
parser.print_help()
|
|
38
|
+
return 0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
raise SystemExit(main())
|
simula/backends.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Backend abstraction for simula.
|
|
2
|
+
|
|
3
|
+
One interface, two adapters. Local-first (llama.cpp + GBNF) but always able to run against
|
|
4
|
+
any OpenAI-compatible endpoint. "Constrained output" is the reliability backbone and is
|
|
5
|
+
implemented differently per backend (PLAN.md #5, PRINCIPLES.md #4).
|
|
6
|
+
|
|
7
|
+
This is a SKELETON: contracts + docstrings. Implement per build phases. stdlib only at the
|
|
8
|
+
contract level; concrete adapters may use `requests`/`httpx` and `sentence-transformers`.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Protocol, Sequence
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class Message:
|
|
19
|
+
role: str # "system" | "user" | "assistant"
|
|
20
|
+
content: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class Contract:
|
|
25
|
+
"""How to constrain structured output. Backend chooses how to honor it.
|
|
26
|
+
|
|
27
|
+
Exactly one of `gbnf_path` / `json_schema` is the primary mechanism; backends fall back
|
|
28
|
+
to a parse-and-repair loop if neither is supported.
|
|
29
|
+
"""
|
|
30
|
+
gbnf_path: Path | None = None # used by llama.cpp native /completion
|
|
31
|
+
json_schema: dict | None = None # used by OpenAI-compat (response_format/tools)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Backend(Protocol):
|
|
35
|
+
"""Text + embedding generation. Implementations MUST guarantee that, when a Contract is
|
|
36
|
+
given, the returned string parses against it (raising on irrecoverable failure)."""
|
|
37
|
+
|
|
38
|
+
def complete(
|
|
39
|
+
self,
|
|
40
|
+
messages: Sequence[Message],
|
|
41
|
+
*,
|
|
42
|
+
contract: Contract | None = None,
|
|
43
|
+
temperature: float = 0.2,
|
|
44
|
+
max_tokens: int = 800,
|
|
45
|
+
) -> str:
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
def embed(self, texts: Sequence[str]) -> list[list[float]]:
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class LlamaCppBackend:
|
|
53
|
+
"""Default, local. Talks to a llama.cpp server (e.g. :18083).
|
|
54
|
+
|
|
55
|
+
Constrained output: prefer the native /completion endpoint with a `grammar` (GBNF) field,
|
|
56
|
+
which guarantees valid structure at decode time. Embeddings stay local (e5-small).
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, endpoint: str, model: str, *, prefer_native_grammar: bool = True) -> None:
|
|
60
|
+
self.endpoint = endpoint
|
|
61
|
+
self.model = model
|
|
62
|
+
self.prefer_native_grammar = prefer_native_grammar
|
|
63
|
+
|
|
64
|
+
def complete(self, messages, *, contract=None, temperature=0.2, max_tokens=800) -> str:
|
|
65
|
+
# Phase 0: implement.
|
|
66
|
+
# - If contract.gbnf_path and prefer_native_grammar: POST /completion with
|
|
67
|
+
# {"prompt": render(messages), "grammar": gbnf_text, "temperature": ..., "n_predict": ...}
|
|
68
|
+
# (Gemma: fold system into the prompt; see PRINCIPLES.md note on system role.)
|
|
69
|
+
# - Else: POST /v1/chat/completions (no hard grammar; rely on repair loop).
|
|
70
|
+
raise NotImplementedError
|
|
71
|
+
|
|
72
|
+
def embed(self, texts) -> list[list[float]]:
|
|
73
|
+
# Phase 1: local e5-small (sentence-transformers) or llama.cpp /embedding.
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class OpenAICompatBackend:
|
|
78
|
+
"""Any OpenAI-compatible endpoint + key + model. Never store the key in config; read env."""
|
|
79
|
+
|
|
80
|
+
def __init__(self, base_url: str, api_key: str, model: str, *, structured_output: str = "json_schema") -> None:
|
|
81
|
+
self.base_url = base_url
|
|
82
|
+
self.api_key = api_key
|
|
83
|
+
self.model = model
|
|
84
|
+
self.structured_output = structured_output # "json_schema" | "tools" | "repair"
|
|
85
|
+
|
|
86
|
+
def complete(self, messages, *, contract=None, temperature=0.2, max_tokens=800) -> str:
|
|
87
|
+
# Phase 0: implement.
|
|
88
|
+
# - structured_output == "json_schema": pass response_format with contract.json_schema.
|
|
89
|
+
# - "tools": expose a single tool whose params == contract.json_schema; force tool_choice.
|
|
90
|
+
# - "repair": free generation + JSON extraction + one repair retry.
|
|
91
|
+
raise NotImplementedError
|
|
92
|
+
|
|
93
|
+
def embed(self, texts) -> list[list[float]]:
|
|
94
|
+
# Default to local e5 even here (decoupling); only use remote embeddings if configured.
|
|
95
|
+
raise NotImplementedError
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def from_config(cfg: dict) -> Backend:
|
|
99
|
+
"""Construct the backend from a parsed simula.toml dict. See simula.toml.example."""
|
|
100
|
+
kind = cfg["backend"]["kind"]
|
|
101
|
+
if kind == "llamacpp":
|
|
102
|
+
c = cfg["backend"]["llamacpp"]
|
|
103
|
+
return LlamaCppBackend(c["endpoint"], c["model"], prefer_native_grammar=c.get("prefer_native_grammar", True))
|
|
104
|
+
if kind == "openai_compat":
|
|
105
|
+
import os
|
|
106
|
+
c = cfg["backend"]["openai_compat"]
|
|
107
|
+
return OpenAICompatBackend(c["base_url"], os.environ[c["api_key_env"]], c["model"],
|
|
108
|
+
structured_output=c.get("structured_output", "json_schema"))
|
|
109
|
+
raise ValueError(f"unknown backend.kind: {kind}")
|
simula/loop.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""The turn loop and the unified entity model.
|
|
2
|
+
|
|
3
|
+
Everything is a Simulacrum = (blueprint, state, memory, contract). A World is a simulacrum of
|
|
4
|
+
place; a Persona is a simulacrum of agent; an NPC is a Persona embedded in a World. The loop is
|
|
5
|
+
the same for both; only blueprint and applied-delta semantics differ. (PLAN.md #1, #3)
|
|
6
|
+
|
|
7
|
+
SKELETON: contracts + docstrings.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from .backends import Backend, Contract, Message
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class Simulacrum:
|
|
20
|
+
"""A world or a persona (or an NPC = persona-in-world)."""
|
|
21
|
+
id: str
|
|
22
|
+
kind: str # "world" | "persona"
|
|
23
|
+
blueprint: dict # validated against schemas/*.schema.json
|
|
24
|
+
state: dict = field(default_factory=dict) # engine-owned source of truth
|
|
25
|
+
# memory + ledger live in sqlite (library.sqlite); referenced by id, not held in RAM.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class TurnResult:
|
|
30
|
+
narration: str
|
|
31
|
+
applied: list[dict] # deltas the engine actually applied (after validation)
|
|
32
|
+
rejected: list[dict] # deltas rejected (invalid against state/ledger), for audit
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
COMMIT_DIRECTIVE = (
|
|
36
|
+
"Commit to a concrete, tangible detail rooted in the texture of this world/persona. "
|
|
37
|
+
"Never retreat into generic fantasy or vagueness. If an action is not feasible, say why, "
|
|
38
|
+
"concretely."
|
|
39
|
+
) # The single highest-value prompt content (PRINCIPLES.md #1).
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def run_turn(
|
|
43
|
+
sim: Simulacrum,
|
|
44
|
+
player_input: str,
|
|
45
|
+
backend: Backend,
|
|
46
|
+
*,
|
|
47
|
+
retrieve, # callable(query, top_k) -> list[chunk]
|
|
48
|
+
ledger, # fact ledger interface (read/append/contradicts)
|
|
49
|
+
transcript_window: list[Message],
|
|
50
|
+
contract: Contract,
|
|
51
|
+
temperature: float = 0.2,
|
|
52
|
+
max_tokens: int = 800,
|
|
53
|
+
) -> TurnResult:
|
|
54
|
+
"""One ORORO-minimal turn (PLAN.md #3):
|
|
55
|
+
|
|
56
|
+
1. Observe - player_input is given.
|
|
57
|
+
2. Retrieve - grounding from materials + relevant ledger facts.
|
|
58
|
+
3. React - assemble a MINIMAL prompt (commit-directive + blueprint spine +
|
|
59
|
+
exemplars + current state + transcript window + input).
|
|
60
|
+
4. Constrain - backend.complete(..., contract=contract) -> guaranteed-parsable TurnOutput.
|
|
61
|
+
5. Validate - check each delta against state + ledger; apply valid, reject invalid.
|
|
62
|
+
6. Persist - caller persists state + appends to ledger/transcript.
|
|
63
|
+
|
|
64
|
+
Returns narration + applied/rejected deltas. Does NOT mutate sqlite directly; the caller
|
|
65
|
+
persists (keeps this function pure-ish and testable for the eval rig).
|
|
66
|
+
"""
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def build_prompt(sim: Simulacrum, player_input: str, grounding: list, state: dict,
|
|
71
|
+
transcript_window: list[Message]) -> list[Message]:
|
|
72
|
+
"""Assemble the minimal prompt. Keep it thin: spine + pointers-grounding, not a big ontology
|
|
73
|
+
(PRINCIPLES.md #2)."""
|
|
74
|
+
raise NotImplementedError
|
simula/py.typed
ADDED
|
File without changes
|
simula/workspace.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Workspace bootstrap. Cross-platform via platformdirs + pathlib (PLAN.md #6, #7).
|
|
2
|
+
|
|
3
|
+
Creates ~/simula-workspace (or platform default) with the standard layout, and never ships
|
|
4
|
+
any corpus: the user supplies their own materials (PLAN.md #9).
|
|
5
|
+
|
|
6
|
+
SKELETON.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
LAYOUT = ["materials", "blueprints", "saves", "evals"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def default_workspace() -> Path:
|
|
16
|
+
"""Platform-appropriate workspace path. Falls back to ~/simula-workspace."""
|
|
17
|
+
try:
|
|
18
|
+
import platformdirs
|
|
19
|
+
return Path(platformdirs.user_data_dir("simula")) / "workspace"
|
|
20
|
+
except Exception:
|
|
21
|
+
return Path.home() / "simula-workspace"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def bootstrap_workspace(path: Path | None = None) -> Path:
|
|
25
|
+
"""Create the workspace folder tree and a starter config if missing. Returns the path."""
|
|
26
|
+
ws = path or default_workspace()
|
|
27
|
+
ws.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
for sub in LAYOUT:
|
|
29
|
+
(ws / sub).mkdir(exist_ok=True)
|
|
30
|
+
cfg = ws / "simula.toml"
|
|
31
|
+
if not cfg.exists():
|
|
32
|
+
# Phase 0: copy simula.toml.example into place.
|
|
33
|
+
pass
|
|
34
|
+
# Phase 1: initialize library.sqlite (sqlite-vec + FTS5 tables).
|
|
35
|
+
return ws
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simula
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lokalno-prvi pogon za sazdavanje i naseljavanje svetova i persona iz korisnikovih materijala.
|
|
5
|
+
Author-email: Peter Ofovik <pedjaurosevic@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/pedjaurosevic/simula
|
|
8
|
+
Project-URL: Repository, https://github.com/pedjaurosevic/simula
|
|
9
|
+
Keywords: llm,simulation,worldbuilding,persona,local-first,llama.cpp,gbnf
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: platformdirs>=4.0
|
|
23
|
+
Provides-Extra: openai
|
|
24
|
+
Requires-Dist: requests>=2.28; extra == "openai"
|
|
25
|
+
Provides-Extra: embeddings
|
|
26
|
+
Requires-Dist: sentence-transformers>=2.2; extra == "embeddings"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# simula
|
|
30
|
+
|
|
31
|
+
**A local-first engine for generating and inhabiting worlds and personas from your own materials.**
|
|
32
|
+
|
|
33
|
+
One engine, two blueprint types (`world` | `persona`), one unified entity model (`Simulacrum`).
|
|
34
|
+
Local-first (llama.cpp + GBNF for hard-constrained output), but always able to run against any
|
|
35
|
+
OpenAI-compatible endpoint.
|
|
36
|
+
|
|
37
|
+
> **Status:** early alpha (Phase 0). The core is still a skeleton — see [`PLAN.md`](PLAN.md) for the
|
|
38
|
+
> implementation phases and [`PRINCIPLES.md`](PRINCIPLES.md) for the empirically derived lessons
|
|
39
|
+
> that drive the design.
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install simula
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick start
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
simula --version
|
|
51
|
+
simula init # create a workspace (materials/ blueprints/ saves/ evals/)
|
|
52
|
+
simula where # print the workspace path
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The workspace lives at a platform-appropriate path (via `platformdirs`), falling back to
|
|
56
|
+
`~/simula-workspace`. **No corpus is ever shipped** — you bring your own materials.
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
Copy `simula.toml.example` into your workspace as `simula.toml` and edit the backend (llama.cpp or
|
|
61
|
+
OpenAI-compatible), embeddings, RAG, and experience mode (`world` | `persona`).
|
|
62
|
+
|
|
63
|
+
## Design in brief
|
|
64
|
+
|
|
65
|
+
- **Constrained output is the reliability backbone:** GBNF on llama.cpp's `/completion`,
|
|
66
|
+
`json_schema` on OpenAI-compatible backends, with a parse-and-repair fallback.
|
|
67
|
+
- **Minimal prompt:** a commit directive + the blueprint spine + pointers into your materials (RAG),
|
|
68
|
+
not a large ontology.
|
|
69
|
+
- **Local-first and private:** embeddings and generation can stay on your own machine.
|
|
70
|
+
- **The engine holds the truth:** the LLM only *proposes* structured changes; the engine validates
|
|
71
|
+
and applies them against authoritative state.
|
|
72
|
+
|
|
73
|
+
## Documentation
|
|
74
|
+
|
|
75
|
+
Full docs: **https://pedjaurosevic.github.io/simula/**
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
simula/__init__.py,sha256=kzT6Q3-BBVJOsnQObJZdh0cGxUMrz4IWyYQ81N664Eo,599
|
|
2
|
+
simula/__main__.py,sha256=xUPAHYMVcbs8v4pMoXs-TDhCe0r3tnadnYIA5irHEAk,1298
|
|
3
|
+
simula/backends.py,sha256=N5kNKtex6cbbff0HOEsPQQhgnoWXEApRDFnWGNrX3G4,4554
|
|
4
|
+
simula/loop.py,sha256=B67WGQpV7PhDtizpyq4OXPR6W5X1hQRqwGflTs34KGc,2948
|
|
5
|
+
simula/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
simula/workspace.py,sha256=_9_9HLmuLo662AGEb5DFzVJTeHzMYKpov6v27arp1iM,1170
|
|
7
|
+
simula-0.1.0.dist-info/licenses/LICENSE,sha256=_RGI15CryF1YN2o9Ridxi3SUssmvCZN7uQSp2OhquvQ,1069
|
|
8
|
+
simula-0.1.0.dist-info/METADATA,sha256=klHIT8NSVfZIIEf4fdHhPpA2qGEFC_FkVsGN5vyv3vQ,3003
|
|
9
|
+
simula-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
10
|
+
simula-0.1.0.dist-info/entry_points.txt,sha256=Z38eQwHhgp4M_p8JKVQOiycl0Hxi2vShYpMnJGMQTAU,48
|
|
11
|
+
simula-0.1.0.dist-info/top_level.txt,sha256=mUmhwzusBASbf93Em7gngQaWRuqwUEN94c00KZL65JI,7
|
|
12
|
+
simula-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Peter Ofovik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
simula
|