skops icon indicating copy to clipboard operation
skops copied to clipboard

Programmatically add sections to model card

Open merveenoyan opened this issue 3 years ago • 26 comments

Currently add only supports injecting values to already existing sections in a template, meanwhile add_plot can append new sections at the end of the model card and write plots. add should also support adding sections and subsections to the model card, without appending if possible. e.g. for default template, if user wants to add a subsection to training procedure (which is in the middle of the card) they should be able to do that without modifying the template themselves.

merveenoyan avatar Aug 03 '22 14:08 merveenoyan

We could also document how users can do this by adding markdown markers. As in, if the user adds this as the content of a section:

this is the main content.

## Subsection

this is the subsection content.

it will be rendered as a subsection.

adrinjalali avatar Sep 05 '22 08:09 adrinjalali

Can anyone assign this to me

0xrushi avatar Oct 06 '22 05:10 0xrushi

@rushic24 Before working on this, let's first discuss how to actually implement this feature, as it's not trivial. If you already have an idea, please describe it here.

BenjaminBossan avatar Oct 06 '22 08:10 BenjaminBossan

Is it neccesary for add to take multiple sections at a time?

If not, we could refactor the method to take in a single section at a time, and add an optional "parent" parameter, that would allow a workflow like:

  1. If parent, add under parent
  2. If section in template, add as normal
  3. Otherwise, add at end

This would need a bit of a refactor for the _generate_card method and the way self._template_sections stores sections, but should be intuitive for the user

E-Aho avatar Oct 06 '22 10:10 E-Aho

Is it neccesary for add to take multiple sections at a time?

I wouldn't change it, honestly, as I believe it's quite useful as is.

Perhaps there is a way, however, to keep it and still add the feature. OTOH:

card = Card(...)
card.add({
    "Model description/Training Procedure/New section": "some text",
})

where "Model description" and "Training Procedure" are existing sections and "New section" is added as a subsection. This still leaves a few open questions, but it's maybe worth exploring.

Going from API to implementation, this is the bigger concern for me though. I don't think the current way we handle model cards lends itself well for this type of dynamic additions. Trying to insert sections with regex looks too brittle to me. I couldn't yet come up with a good solution that works with the current model card implementation.

BenjaminBossan avatar Oct 06 '22 11:10 BenjaminBossan

Instead of string we can enable markdown rendering for card values like

card = Card(...)
card.add({
    "Model description/Training Procedure/New section": """
    
    # Test
    sdjksdksdjsjks
    ## Subsection
    """,
})

0xrushi avatar Oct 06 '22 13:10 0xrushi

Perhaps there is a way, however, to keep it and still add the feature. OTOH: ...

I think an API like that could be powerful, but it would need some thought on handling edge cases and how to keep it intuitive for the users.

I don't think the current way we handle model cards lends itself well for this type of dynamic additions. Trying to insert sections with regex looks too brittle to me.

I definitely agree, I don't think regex would be the right approach.

I think to have robust, programatic dynamic additions, it might need a bit of an overhaul, perhaps using a markdown util library to parse the template and keep a running .md structure in a python object we can work with?

E-Aho avatar Oct 06 '22 14:10 E-Aho

I think to have robust, programatic dynamic additions, it might need a bit of an overhaul, perhaps using a markdown util library to parse the template and keep a running .md structure in a python object we can work with?

Yes, that's something that I have also thought about. Having some container type in Python to hold the content of the model card instead of a template would give us a couple of advantages. It would be much easier to add, delete, or otherwise modify the model card.

Parsing the template didn't look trivial to me, maybe I just missed something but it looked like it would require quite some effort. Not using a template at all would be a solution, instead having aforementioned container be pre-filled by default with the same sections as the template, but the template gives users a nice thing to look at for guidance, so I'm not sure.

BenjaminBossan avatar Oct 07 '22 08:10 BenjaminBossan

Instead of string we can enable markdown rendering for card values

Maybe I don't quite understand your suggestion, but your example should already work right now.

BenjaminBossan avatar Oct 07 '22 08:10 BenjaminBossan

Parsing the template didn't look trivial to me, maybe I just missed something but it looked like it would require quite some effort. Not using a template at all would be a solution, instead having aforementioned container be pre-filled by default with the same sections as the template, but the template gives users a nice thing to look at for guidance, so I'm not sure.

It wouldn't be trivial, but there are some nice libraries that parse .md into a Python Object, which you can extend and work with. Python-Markdown comes to mind, and python markdown comes with utilities to edit and add to the element tree. Unsure if adding a dependency to get this functionality is the right call, but I think writing a robust .md parser from scratch that converts to an element tree we can work with and then recompiles to md would be overkill.

I could play with this and try to get a proof of concept up and running if it's a direction that you think might be worth exploring @BenjaminBossan

E-Aho avatar Oct 07 '22 11:10 E-Aho

I could play with this and try to get a proof of concept up and running if it's a direction that you think might be worth exploring

Personally, I think having this feature could be useful, so having a POC would be nice, but before you spend too much time on it, I would like to hear if the other @skops-dev/maintainers agree.

BenjaminBossan avatar Oct 07 '22 11:10 BenjaminBossan

Instead of string we can enable markdown rendering for card values like

card = Card(...)
card.add({
    "Model description/Training Procedure/New section": """
    
    # Test
    sdjksdksdjsjks
    ## Subsection
    """,
})

Sooo, this won't fix it ?

0xrushi avatar Oct 07 '22 22:10 0xrushi

So I've played with it a bit more, and I think there are two main ways forward if we want the functionality to add sections to specific areas of existing Markdown:

  1. Use a md parser that makes use of an etree under the hood, like Python-Markdown, then after adding to it recompile back to .md from HTML/XML with something like markdownify, which feels a little heavy handed but would work.

  2. Write our own methods to parse the .md template into an object we can work with and then recompile in a similar way as is being done now.

I think either method would be fine, but looking at the implementation of how Python-Markdown converts .md to an etree, writing a well specced parser from scratch would likely be a big task. However, option 1 would need a lot of testing make sure that everything works after the the multiple conversion steps.

There is still the option of just inserting into the markdown string with some regex logic, which having seen what the alternatives would look like, doesn't sound as bad to me

E-Aho avatar Oct 08 '22 16:10 E-Aho

Sooo, this won't fix it ?

Hmm, I think you need to elaborate further what the result of this call would be, at least to me it's not clear.

BenjaminBossan avatar Oct 10 '22 08:10 BenjaminBossan

So I've played with it a bit more, and I think there are two main ways forward if we want the functionality to add sections to specific areas of existing Markdown:

  1. Use a md parser that makes use of an etree under the hood, like Python-Markdown, then after adding to it recompile back to .md from HTML/XML with something like markdownify, which feels a little heavy handed but would work.

Yes, I was afraid that it would be quite heavy handed in the end, I'm really not sure if it's worth the cost.

  1. Write our own methods to parse the .md template into an object we can work with and then recompile in a similar way as is being done now.

There is still the option of just inserting into the markdown string with some regex logic

Both of these will probably be quite easy for 95% of cases but getting all the edge cases right will most likely require a lot of work. As with the first option, I feel it's too costly to add this.

Another method that I considered would be:

  1. Drop the markdown parsing part completely. Instead, have some Python data structure that contains the content (dicts/lists) and then render it to markdown, which would have most of the benefits but circumvent the difficulties being discussed.

If we do this, we need to think about the advantages we would lose from having a md template. The template provide some defaults, but we can add those to the solution 3 as well. The template can also be inspected easily just in a browser or text editor, which this solution cannot provide (or maybe...).

BenjaminBossan avatar Oct 10 '22 08:10 BenjaminBossan

I think a major downside to dropping .md templating is that is the way it seems most other HF libs handle making cards right now ( e.g here is how repo and model cards in HF_hub are handled ).

In my opinion, it would be really unintuitive if skops used a completely different way to make and template cards than other HF repos

E-Aho avatar Oct 10 '22 09:10 E-Aho

I think a major downside to dropping .md templating is that is the way it seems most other HF libs handle making cards right now

I can see that argument, although I believe most users don't care too much about how the model card is generated under the hood, they probably care more about features and the final markdown file. However, it is true that if we diverge too far, it might become more difficult to keep parity with the "normal" way model cards are handled on HF.

BenjaminBossan avatar Oct 10 '22 15:10 BenjaminBossan

There's also an argument for having model cards in scikit-learn itself. So I'd say we shouldn't worry too much about how things are done in other HF libs.

adrinjalali avatar Oct 11 '22 14:10 adrinjalali

Hey! Sorry for being slow to reply, I've been working on a few other bits but I'm getting close to finishing with those, and I'd be happy to start getting a POC up for this.

Currently thinking of trying out a JSON/XML type template system, I think that would be much easier to parse into Python and work with programatically before rendering to .md

E-Aho avatar Oct 15 '22 18:10 E-Aho

hmm, that sounds like a HUGE undertaking though 🤔

WDYT @BenjaminBossan

adrinjalali avatar Oct 17 '22 08:10 adrinjalali

I agree, it really sounds like the cost would exceed the benefit in this case.

This is really a tough question. I have thought about it and here are some suggestions I came up with:

  1. Extremely simple parser

Write an extremely simple parser without any external dependency. It doesn't care about edge cases and works with the default we provide but not much more.

  1. Add second card class

It would be easier to say we add another method to create cards that is independent of the template (but still can have the same default sections + contents). This new class would use some Python data structures that would make working with it much easier and enable dynamically adding sections.

The big disadvantage here would be that now we have two classes that do very similar but not identical things (some method supported on one but not the other). This is highly undesirable.

Honestly, if we could start, I would probably advocate for the second variant and not use templates at all.

  1. Leave the current state

For just the feature of adding sections dynamically, the effort might not be worth it. However, I can imagine that our current, inflexible implementation will cause other headaches in the future.

BenjaminBossan avatar Oct 17 '22 08:10 BenjaminBossan

Good points.

It seems we're adding functionalities which are supposed to be added by changing the template. Like, with the current design, if users want to add sections, they should actually write a new template, but that's too much work and I don't think we want users to do that.

Therefore I'm with @BenjaminBossan here that maybe using templates is not the best idea here afterall?

adrinjalali avatar Oct 17 '22 09:10 adrinjalali

I agree with you both honestly.

It does feel like a pretty major rework would be needed to allow programatic additions to a template, and if we're at the point of thinking of second card class, or adding in multiple new dependencies and a lot of extra logic, I feel like it's a good sign the current implementation with templating is just too inflexible for what we want to do.

Patching what is here right now would just add extra tech debt imo, I think it would be a good idea to consider moving away from having .md templates all together.

E-Aho avatar Oct 17 '22 16:10 E-Aho

It seems we're adding functionalities which are supposed to be added by changing the template. Like, with the current design, if users want to add sections, they should actually write a new template, but that's too much work and I don't think we want users to do that.

Yes, it just sounds too cumbersome to do this, I'd imagine many users would just dump everything to the end of the document instead. However, once a template has been modified, it can be shared easily, it would be a pity to lose that ability.

Patching what is here right now would just add extra tech debt imo, I think it would be a good idea to consider moving away from having .md templates all together.

There is probably no better time than now to still make such a change :)

BenjaminBossan avatar Oct 18 '22 12:10 BenjaminBossan

The benefit of templates is that it nudges people to include certain information in the model card. But we can achieve the same by making sure those bits which are not filled are programmatically generated as empty and need work at the end of the generated file.

Maybe we could have:

  • a ModeCard class which takes the same information as provided by the jinja templates in terms of what sections are required/suggested, but in an easier format, with sane defaults so that most users won't have to provide that
  • generate the whole thing programmatically, and if something's not provided, add them to the end of the card as missing

WDYT?

adrinjalali avatar Oct 19 '22 08:10 adrinjalali

Maybe we could have:

  • a ModeCard class which takes the same information as provided by the jinja templates in terms of what sections are required/suggested, but in an easier format, with sane defaults so that most users won't have to provide that

  • generate the whole thing programmatically, and if something's not provided, add them to the end of the card as missing

WDYT?

Sounds good to me, at least we could give it a try and see if we feel better about it than the current approach.

BenjaminBossan avatar Oct 19 '22 09:10 BenjaminBossan

Regarding the question of parsing a markdown file to a Python data structure, I wonder if we can make use of pandoc scripting to achieve this. Here is their doc on it: https://pandoc.org/scripting-1.12.html. The advantage of using pandoc is that it's widely used, so probably very bug free, and they already do the difficult parsing step. On top, pandoc could be used to read non-markdown as well.

Pandoc is haskell based, but they provide a Python package that can interpret the representation created by pandoc called pandocfilters. I haven't explored that option in depth, but it looks like it should be possible to use pandoc > pandocfilter > model card Python data structure.

Maybe we can experiment with this and see if it works. If it does, we could perhaps move that functionality to a separate package in the skops-dev space, so that users who don't need it don't suddenly have a pandoc dependency.

To give you an idea how a parsed document looks like, I ran pandoc on one of the model cards used in our unit tests. It looks a bit verbose, not sure if the output can be simplified, but regardless, it could be workable.

Output of pandoc -t json -s README.md | json_pp

{
   "blocks" : [
      {
         "c" : [
            1,
            [
               "model-description",
               [],
               []
            ],
            [
               {
                  "c" : "Model",
                  "t" : "Str"
               },
               {
                  "t" : "Space"
               },
               {
                  "c" : "description",
                  "t" : "Str"
               }
            ]
         ],
         "t" : "Header"
      },
      {
         "c" : [
            {
               "c" : "[More",
               "t" : "Str"
            },
            {
               "t" : "Space"
            },
            {
               "c" : "Information",
               "t" : "Str"
            },
            {
               "t" : "Space"
            },
            {
               "c" : "Needed]",
               "t" : "Str"
            }
         ],
         "t" : "Para"
      },
      {
         "c" : [
            2,
            [
               "intended-uses-limitations",
               [],
               []
            ],
            [
               {
                  "c" : "Intended",
                  "t" : "Str"
               },
               {
                  "t" : "Space"
               },
               {
                  "c" : "uses",
                  "t" : "Str"
               },
               {
                  "t" : "Space"
               },
               {
                  "c" : "&",
                  "t" : "Str"
               },
               {
                  "t" : "Space"
               },
               {
                  "c" : "limitations",
                  "t" : "Str"
               }
            ]
         ],
         "t" : "Header"
      },
      {
         "c" : [
            {
               "c" : "[More",
               "t" : "Str"
            },
            {
               "t" : "Space"
            },
            {
               "c" : "Information",
               "t" : "Str"
            },
            {
               "t" : "Space"
            },
            {
               "c" : "Needed]",
               "t" : "Str"
            }
         ],
         "t" : "Para"
      },
      {
         "c" : [
            2,
            [
               "training-procedure",
               [],
               []
            ],
            [
               {
                  "c" : "Training",
                  "t" : "Str"
               },
               {
                  "t" : "Space"
               },
               {
                  "c" : "Procedure",
                  "t" : "Str"
               }
            ]
         ],
         "t" : "Header"
      },
      {
         "c" : [
            3,
            [
               "hyperparameters",
               [],
               []
            ],
            [
               {
                  "c" : "Hyperparameters",
                  "t" : "Str"
               }
            ]
         ],
         "t" : "Header"
      },
      {
         "c" : [
            {
               "c" : "The",
               "t" : "Str"
            },
            {
               "t" : "Space"
            },
            {
               "c" : "model",
               "t" : "Str"
            },
            {
               "t" : "Space"
            },
            {
               "c" : "is",
               "t" : "Str"
            },
            {
               "t" : "Space"
            },
            {
               "c" : "trained",
               "t" : "Str"
            },
            {
               "t" : "Space"
            },
            {
               "c" : "with",
               "t" : "Str"
            },
            {
               "t" : "Space"
            },
            {
               "c" : "below",
               "t" : "Str"
            },
            {
               "t" : "Space"
            },
            {
               "c" : "hyperparameters.",
               "t" : "Str"
            }
         ],
         "t" : "Para"
      },
      {
         "c" : [
            "html",
            "
" ], "t" : "RawBlock" }, { "c" : [ { "c" : [ "html", "" ], "t" : "RawInline" }, { "t" : "Space" }, { "c" : "Click", "t" : "Str" }, { "t" : "Space" }, { "c" : "to", "t" : "Str" }, { "t" : "Space" }, { "c" : "expand", "t" : "Str" }, { "t" : "Space" }, { "c" : [ "html", "" ], "t" : "RawInline" } ], "t" : "Para" }, { "c" : [ [], [ { "t" : "AlignDefault" }, { "t" : "AlignDefault" } ], [ 0, 0 ], [ [ { "c" : [ { "c" : "Hyperparameter", "t" : "Str" } ], "t" : "Plain" } ], [ { "c" : [ { "c" : "Value", "t" : "Str" } ], "t" : "Plain" } ] ], [ [ [ { "c" : [ { "c" : "copy_X", "t" : "Str" } ], "t" : "Plain" } ], [ { "c" : [ { "c" : "True", "t" : "Str" } ], "t" : "Plain" } ] ], [ [ { "c" : [ { "c" : "fit_intercept", "t" : "Str" } ], "t" : "Plain" } ], [ { "c" : [ { "c" : "True", "t" : "Str" } ], "t" : "Plain" } ] ], [ [ { "c" : [ { "c" : "n_jobs", "t" : "Str" } ], "t" : "Plain" } ], [] ], [ [ { "c" : [ { "c" : "normalize", "t" : "Str" } ], "t" : "Plain" } ], [ { "c" : [ { "c" : "deprecated", "t" : "Str" } ], "t" : "Plain" } ] ], [ [ { "c" : [ { "c" : "positive", "t" : "Str" } ], "t" : "Plain" } ], [ { "c" : [ { "c" : "False", "t" : "Str" } ], "t" : "Plain" } ] ] ] ], "t" : "Table" }, { "c" : [ "html", "
" ], "t" : "RawBlock" }, { "c" : [ 3, [ "model-plot", [], [] ], [ { "c" : "Model", "t" : "Str" }, { "t" : "Space" }, { "c" : "Plot", "t" : "Str" } ] ], "t" : "Header" }, { "c" : [ { "c" : "The", "t" : "Str" }, { "t" : "Space" }, { "c" : "model", "t" : "Str" }, { "t" : "Space" }, { "c" : "plot", "t" : "Str" }, { "t" : "Space" }, { "c" : "is", "t" : "Str" }, { "t" : "Space" }, { "c" : "below.", "t" : "Str" } ], "t" : "Para" }, { "c" : [ "html", "" ], "t" : "RawBlock" }, { "c" : [ [ "sk-container-id-1", [ "sk-top-container" ], [ [ "style", "overflow: auto;" ] ] ], [ { "c" : [ [ "", [ "sk-text-repr-fallback" ], [] ], [ { "c" : [ "html", "
LinearRegression()
" ], "t" : "RawBlock" }, { "c" : [ { "c" : [ "html", "" ], "t" : "RawInline" }, { "c" : "In", "t" : "Str" }, { "t" : "Space" }, { "c" : "a", "t" : "Str" }, { "t" : "Space" }, { "c" : "Jupyter", "t" : "Str" }, { "t" : "Space" }, { "c" : "environment,", "t" : "Str" }, { "t" : "Space" }, { "c" : "please", "t" : "Str" }, { "t" : "Space" }, { "c" : "rerun", "t" : "Str" }, { "t" : "Space" }, { "c" : "this", "t" : "Str" }, { "t" : "Space" }, { "c" : "cell", "t" : "Str" }, { "t" : "Space" }, { "c" : "to", "t" : "Str" }, { "t" : "Space" }, { "c" : "show", "t" : "Str" }, { "t" : "Space" }, { "c" : "the", "t" : "Str" }, { "t" : "Space" }, { "c" : "HTML", "t" : "Str" }, { "t" : "Space" }, { "c" : "representation", "t" : "Str" }, { "t" : "Space" }, { "c" : "or", "t" : "Str" }, { "t" : "Space" }, { "c" : "trust", "t" : "Str" }, { "t" : "Space" }, { "c" : "the", "t" : "Str" }, { "t" : "Space" }, { "c" : "notebook.", "t" : "Str" }, { "t" : "Space" }, { "c" : [ "html", "
" ], "t" : "RawInline" }, { "c" : "On", "t" : "Str" }, { "t" : "Space" }, { "c" : "GitHub,", "t" : "Str" }, { "t" : "Space" }, { "c" : "the", "t" : "Str" }, { "t" : "Space" }, { "c" : "HTML", "t" : "Str" }, { "t" : "Space" }, { "c" : "representation", "t" : "Str" }, { "t" : "Space" }, { "c" : "is", "t" : "Str" }, { "t" : "Space" }, { "c" : "unable", "t" : "Str" }, { "t" : "Space" }, { "c" : "to", "t" : "Str" }, { "t" : "Space" }, { "c" : "render,", "t" : "Str" }, { "t" : "Space" }, { "c" : "please", "t" : "Str" }, { "t" : "Space" }, { "c" : "try", "t" : "Str" }, { "t" : "Space" }, { "c" : "loading", "t" : "Str" }, { "t" : "Space" }, { "c" : "this", "t" : "Str" }, { "t" : "Space" }, { "c" : "page", "t" : "Str" }, { "t" : "Space" }, { "c" : "with", "t" : "Str" }, { "t" : "Space" }, { "c" : "nbviewer.org.", "t" : "Str" }, { "c" : [ "html", "
" ], "t" : "RawInline" } ], "t" : "Plain" } ] ], "t" : "Div" }, { "c" : [ [ "", [ "sk-container" ], [ [ "hidden", "" ] ] ], [ { "c" : [ [ "", [ "sk-item" ], [] ], [ { "c" : [ [ "", [ "sk-estimator", "sk-toggleable" ], [] ], [ { "c" : [ { "c" : [ "html", "" ], "t" : "RawInline" }, { "c" : [ "html", "" ], "t" : "RawInline" } ], "t" : "Plain" }, { "c" : [ [ "", [ "sk-toggleable__content" ], [] ], [ { "c" : [ "html", "
LinearRegression()
" ], "t" : "RawBlock" } ] ], "t" : "Div" } ] ], "t" : "Div" } ] ], "t" : "Div" } ] ], "t" : "Div" } ] ], "t" : "Div" }, { "c" : [ 2, [ "evaluation-results", [], [] ], [ { "c" : "Evaluation", "t" : "Str" }, { "t" : "Space" }, { "c" : "Results", "t" : "Str" } ] ], "t" : "Header" }, { "c" : [ { "c" : "You", "t" : "Str" }, { "t" : "Space" }, { "c" : "can", "t" : "Str" }, { "t" : "Space" }, { "c" : "find", "t" : "Str" }, { "t" : "Space" }, { "c" : "the", "t" : "Str" }, { "t" : "Space" }, { "c" : "details", "t" : "Str" }, { "t" : "Space" }, { "c" : "about", "t" : "Str" }, { "t" : "Space" }, { "c" : "evaluation", "t" : "Str" }, { "t" : "Space" }, { "c" : "process", "t" : "Str" }, { "t" : "Space" }, { "c" : "and", "t" : "Str" }, { "t" : "Space" }, { "c" : "the", "t" : "Str" }, { "t" : "Space" }, { "c" : "evaluation", "t" : "Str" }, { "t" : "Space" }, { "c" : "results.", "t" : "Str" } ], "t" : "Para" }, { "c" : [ [], [ { "t" : "AlignDefault" }, { "t" : "AlignDefault" } ], [ 0, 0 ], [ [ { "c" : [ { "c" : "Metric", "t" : "Str" } ], "t" : "Plain" } ], [ { "c" : [ { "c" : "Value", "t" : "Str" } ], "t" : "Plain" } ] ], [] ], "t" : "Table" }, { "c" : [ 1, [ "how-to-get-started-with-the-model", [], [] ], [ { "c" : "How", "t" : "Str" }, { "t" : "Space" }, { "c" : "to", "t" : "Str" }, { "t" : "Space" }, { "c" : "Get", "t" : "Str" }, { "t" : "Space" }, { "c" : "Started", "t" : "Str" }, { "t" : "Space" }, { "c" : "with", "t" : "Str" }, { "t" : "Space" }, { "c" : "the", "t" : "Str" }, { "t" : "Space" }, { "c" : "Model", "t" : "Str" } ] ], "t" : "Header" }, { "c" : [ { "c" : "[More", "t" : "Str" }, { "t" : "Space" }, { "c" : "Information", "t" : "Str" }, { "t" : "Space" }, { "c" : "Needed]", "t" : "Str" } ], "t" : "Para" }, { "c" : [ 1, [ "model-card-authors", [], [] ], [ { "c" : "Model", "t" : "Str" }, { "t" : "Space" }, { "c" : "Card", "t" : "Str" }, { "t" : "Space" }, { "c" : "Authors", "t" : "Str" } ] ], "t" : "Header" }, { "c" : [ { "c" : "This", "t" : "Str" }, { "t" : "Space" }, { "c" : "model", "t" : "Str" }, { "t" : "Space" }, { "c" : "card", "t" : "Str" }, { "t" : "Space" }, { "c" : "is", "t" : "Str" }, { "t" : "Space" }, { "c" : "written", "t" : "Str" }, { "t" : "Space" }, { "c" : "by", "t" : "Str" }, { "t" : "Space" }, { "c" : "following", "t" : "Str" }, { "t" : "Space" }, { "c" : "authors:", "t" : "Str" } ], "t" : "Para" }, { "c" : [ { "c" : "[More", "t" : "Str" }, { "t" : "Space" }, { "c" : "Information", "t" : "Str" }, { "t" : "Space" }, { "c" : "Needed]", "t" : "Str" } ], "t" : "Para" }, { "c" : [ 1, [ "model-card-contact", [], [] ], [ { "c" : "Model", "t" : "Str" }, { "t" : "Space" }, { "c" : "Card", "t" : "Str" }, { "t" : "Space" }, { "c" : "Contact", "t" : "Str" } ] ], "t" : "Header" }, { "c" : [ { "c" : "You", "t" : "Str" }, { "t" : "Space" }, { "c" : "can", "t" : "Str" }, { "t" : "Space" }, { "c" : "contact", "t" : "Str" }, { "t" : "Space" }, { "c" : "the", "t" : "Str" }, { "t" : "Space" }, { "c" : "model", "t" : "Str" }, { "t" : "Space" }, { "c" : "card", "t" : "Str" }, { "t" : "Space" }, { "c" : "authors", "t" : "Str" }, { "t" : "Space" }, { "c" : "through", "t" : "Str" }, { "t" : "Space" }, { "c" : "following", "t" : "Str" }, { "t" : "Space" }, { "c" : "channels:", "t" : "Str" }, { "t" : "SoftBreak" }, { "c" : "[More", "t" : "Str" }, { "t" : "Space" }, { "c" : "Information", "t" : "Str" }, { "t" : "Space" }, { "c" : "Needed]", "t" : "Str" } ], "t" : "Para" }, { "c" : [ 1, [ "citation", [], [] ], [ { "c" : "Citation", "t" : "Str" } ] ], "t" : "Header" }, { "c" : [ { "c" : "Below", "t" : "Str" }, { "t" : "Space" }, { "c" : "you", "t" : "Str" }, { "t" : "Space" }, { "c" : "can", "t" : "Str" }, { "t" : "Space" }, { "c" : "find", "t" : "Str" }, { "t" : "Space" }, { "c" : "information", "t" : "Str" }, { "t" : "Space" }, { "c" : "related", "t" : "Str" }, { "t" : "Space" }, { "c" : "to", "t" : "Str" }, { "t" : "Space" }, { "c" : "citation.", "t" : "Str" } ], "t" : "Para" }, { "c" : [ { "c" : [ { "c" : "BibTeX:", "t" : "Str" } ], "t" : "Strong" } ], "t" : "Para" }, { "c" : [ [ "", [], [] ], "[More Information Needed]" ], "t" : "CodeBlock" } ], "meta" : {}, "pandoc-api-version" : [ 1, 17, 5, 4 ] }

BenjaminBossan avatar Nov 25 '22 11:11 BenjaminBossan

That sounds good to me!

adrinjalali avatar Nov 28 '22 10:11 adrinjalali

@BenjaminBossan TIL of pandoc 🤩 seems very neat!

merveenoyan avatar Nov 28 '22 12:11 merveenoyan

I think we can consider this being solved by #203. The parsing of model cards is yet to come, but that was not part of the initial issue.

BenjaminBossan avatar Jan 18 '23 14:01 BenjaminBossan